# Always print this out before your assignment
sessionInfo()
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7

Matrix products: default
BLAS:   /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods  
[7] base     

other attached packages:
 [1] ggridges_0.5.3             glmnetUtils_1.1.8         
 [3] glmnet_4.1-3               Matrix_1.3-4              
 [5] plotROC_2.2.1              scales_1.1.1              
 [7] tidyquant_1.0.3            quantmod_0.4.18           
 [9] TTR_0.24.2                 PerformanceAnalytics_2.0.4
[11] xts_0.12.1                 zoo_1.8-9                 
[13] plotly_4.10.0              viridis_0.6.2             
[15] viridisLite_0.4.0          pastecs_1.3.21            
[17] kableExtra_1.3.4           lubridate_1.8.0           
[19] rsample_0.1.1              ggthemes_4.2.4            
[21] ggrepel_0.9.1              here_1.0.1                
[23] fs_1.5.0                   forcats_0.5.1             
[25] stringr_1.4.0              dplyr_1.0.7               
[27] purrr_0.3.4                readr_2.1.0               
[29] tidyr_1.1.4                tibble_3.1.6              
[31] ggplot2_3.3.5              tidyverse_1.3.1           
[33] knitr_1.36                

loaded via a namespace (and not attached):
 [1] colorspace_2.0-2  ellipsis_0.3.2    rprojroot_2.0.2  
 [4] rstudioapi_0.13   listenv_0.8.0     furrr_0.2.3      
 [7] farver_2.1.0      fansi_0.5.0       xml2_1.3.2       
[10] codetools_0.2-18  splines_4.1.2     jsonlite_1.7.2   
[13] broom_0.7.10      dbplyr_2.1.1      compiler_4.1.2   
[16] httr_1.4.2        backports_1.4.0   assertthat_0.2.1 
[19] fastmap_1.1.0     lazyeval_0.2.2    cli_3.1.0        
[22] htmltools_0.5.2   tools_4.1.2       gtable_0.3.0     
[25] glue_1.5.0        Rcpp_1.0.7        cellranger_1.1.0 
[28] jquerylib_0.1.4   vctrs_0.3.8       svglite_2.0.0    
[31] nlme_3.1-153      iterators_1.0.13  crosstalk_1.2.0  
[34] xfun_0.28         globals_0.14.0    rvest_1.0.2      
[37] lifecycle_1.0.1   future_1.23.0     hms_1.1.1        
[40] parallel_4.1.2    yaml_2.2.1        curl_4.3.2       
[43] gridExtra_2.3     sass_0.4.0        stringi_1.7.5    
[46] highr_0.9         foreach_1.5.1     boot_1.3-28      
[49] shape_1.4.6       rlang_0.4.12      pkgconfig_2.0.3  
[52] systemfonts_1.0.3 evaluate_0.14     lattice_0.20-45  
[55] htmlwidgets_1.5.4 labeling_0.4.2    tidyselect_1.1.1 
[58] parallelly_1.29.0 plyr_1.8.6        magrittr_2.0.1   
[61] R6_2.5.1          generics_0.1.1    DBI_1.1.1        
[64] pillar_1.6.4      haven_2.4.3       withr_2.4.2      
[67] mgcv_1.8-38       survival_3.2-13   modelr_0.1.8     
[70] crayon_1.4.2      Quandl_2.11.0     utf8_1.2.2       
[73] tzdb_0.2.0        rmarkdown_2.11    grid_4.1.2       
[76] readxl_1.3.1      data.table_1.14.2 reprex_2.0.1     
[79] digest_0.6.28     webshot_0.5.2     munsell_0.5.0    
[82] bslib_0.3.1       quadprog_1.5-8   
getwd()
[1] "/Users/ryanradcliff/Documents/BUS696/BROCODE_Final_Project"

# load all your libraries in this chunk 
library('tidyverse')
library("fs")
library('here')
library('dplyr')
library('tidyverse')
library('ggplot2')
library('ggrepel')
library('ggthemes')
library('forcats')
library('rsample')
library('lubridate')
library('ggthemes')
library('kableExtra')
library('pastecs')
library('viridis')
library('plotly')
library('tidyquant')
library('scales')
library("gdata")
gdata: read.xls support for 'XLS' (Excel 97-2004) files
gdata: ENABLED.

gdata: read.xls support for 'XLSX' (Excel 2007+) files
gdata: ENABLED.

Attaching package: ‘gdata’

The following objects are masked from ‘package:xts’:

    first, last

The following objects are masked from ‘package:pastecs’:

    first, last

The following objects are masked from ‘package:dplyr’:

    combine, first, last

The following object is masked from ‘package:purrr’:

    keep

The following object is masked from ‘package:stats’:

    nobs

The following object is masked from ‘package:utils’:

    object.size

The following object is masked from ‘package:base’:

    startsWith
# note, do not run install.packages() inside a code chunk. install them in the console outside of a code chunk. 

Part 1 - Final Project Cleaning and Summary Statistics

1a) Loading data


#Reading the data in and doing minor initial cleaning in the function call
#Reproducible data analysis should avoid all automatic string to factor conversions.
#strip.white removes white space 
#na.strings is a substitution so all that have "" will = na
data <- read.csv(here::here("final_project", "donor_data.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

1b) Fixing the wonky DOB & Data cleanup


#(Birthdate and Age, ID as a number)adding DOB (Age/Spouse Age) in years columns and adding two fields for assignment and number of children and number of degrees
dataclean <- data %>%
  mutate(Birthdate = ifelse(Birthdate == "0001-01-01", NA, Birthdate)) %>%
  mutate(Birthdate = mdy(Birthdate)) %>%
  mutate(Age = as.numeric(floor(interval(start= Birthdate, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(Spouse.Birthdate = ifelse(Spouse.Birthdate == "0001-01-01", NA, Spouse.Birthdate)) %>%
  mutate(Spouse.Birthdate = mdy(Spouse.Birthdate)) %>%
  mutate(Spouse.Age = as.numeric(floor(interval(start= Spouse.Birthdate,
                                                end=Sys.Date())/duration(n=1, unit="years")))) %>%
  mutate(ID = as.numeric(ID)) %>% 
  mutate(Assignment_flag = ifelse(is.na(Assignment.Number), 0,1)) %>% 
  mutate( No_of_Children = ifelse(is.na(Child.1.ID),0,
                            ifelse(is.na(Child.2.ID),1,2)))%>%
 mutate(ID = as.numeric(ID)) %>% 
    mutate( nmb_degree = ifelse(is.na(Degree.Type.1),0,
                            ifelse(is.na(Degree.Type.2),1,2))) %>%
#conferral dates
  mutate(Conferral.Date.1 = ifelse(Conferral.Date.1 == "0001-01-01", NA, Conferral.Date.1)) %>%
  mutate(Conferral.Date.1 = mdy(Conferral.Date.1)) %>%
  mutate(Conferral.Date.1.Age = as.numeric(floor(interval(start= Conferral.Date.1, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Conferral.Date.2 = ifelse(Conferral.Date.2 == "0001-01-01", NA, Conferral.Date.2)) %>%
  mutate(Conferral.Date.2 = mdy(Conferral.Date.2)) %>%
  mutate(Conferral.Date.2.Age = as.numeric(floor(interval(start= Conferral.Date.2, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
  mutate(Last.Contact.By.Anyone = ifelse(Last.Contact.By.Anyone == "0001-01-01", NA, Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.By.Anyone = mdy(Last.Contact.By.Anyone)) %>%
  mutate(Last.Contact.Age = as.numeric(floor(interval(start= Last.Contact.By.Anyone, end=Sys.Date())/duration(n=1, unit="years")))) %>%
  
 mutate(HH.First.Gift.Date = ifelse(HH.First.Gift.Date == "0001-01-01", NA, HH.First.Gift.Date)) %>%
  mutate(HH.First.Gift.Date = mdy(HH.First.Gift.Date)) %>%
mutate(HH.First.Gift.Age = as.numeric(floor(interval(start= HH.First.Gift.Date, end=Sys.Date())/duration(n=1, unit="years")))) %>% 
#major gift 
  mutate(major_gifter = ifelse(Lifetime.Giving > 50000, 1,0) %>% factor(., levels = c("0","1"))) %>%
#splitting up the age into ranges and creating category for easy visualization 
  mutate(age_range = 
    ifelse(Age %in% 10:19, "10 < 20 years old",
    ifelse(Age %in% 20:29, "20 < 30 years old", 
    ifelse(Age %in% 30:39, "30 < 40 years old",
    ifelse(Age %in% 40:49, "40 < 50 years old",
    ifelse(Age %in% 50:59, "50 < 60 years old",
    ifelse(Age %in% 60:69, "60 < 70 years old",
    ifelse(Age %in% 70:79, "70 < 80 years old",
    ifelse(Age %in% 80:89, "80 < 90 years old",
    ifelse(Age %in% 90:120, "90+ years old",
    NA)))))))))) %>%
#creating a region column using the county data and the OMB MSA (Metropolitan Statistical Area) definitions
  mutate(region = 
    ifelse(County == "San Luis Obispo" & State == "CA", "So Cal",
    ifelse(County == "Kern" & State == "CA", "So Cal",
    ifelse(County == "San Bernardino" & State == "CA", "So Cal",
    ifelse(County == "Santa Barbara" & State == "CA", "So Cal",
    ifelse(County == "Ventura" & State == "CA", "So Cal",
    ifelse(County == "Los Angeles" & State == "CA", "So Cal",
    ifelse(County == "Orange" & State == "CA", "So Cal",
    ifelse(County == "Riverside" & State == "CA", "So Cal",
    ifelse(County == "San Diego" & State == "CA", "So Cal",
    ifelse(County == "Imperial" & State == "CA", "So Cal",
    ifelse(County == "King" & State == "WA", "Seattle",
    ifelse(County == "Snohomish" & State == "WA", "Seattle",
    ifelse(County == "Pierce" & State == "WA", "Seattle",
    ifelse(County == "Clackamas" & State == "OR", "Portland",
    ifelse(County == "Columbia" & State == "OR", "Portland",
    ifelse(County == "Multnomah" & State == "OR", "Portland",
    ifelse(County == "Washington" & State == "OR", "Portland",
    ifelse(County == "Yamhill" & State == "OR", "Portland",
    ifelse(County == "Clark" & State == "WA", "Portland",
    ifelse(County == "Skamania" & State == "WA", "Portland",
    ifelse(County == "Denver" & State == "CO", "Denver",
    ifelse(County == "Arapahoe" & State == "CO", "Denver",
    ifelse(County == "Jefferson" & State == "CO", "Denver",
    ifelse(County == "Adams" & State == "CO", "Denver",
    ifelse(County == "Douglas" & State == "CO", "Denver",
    ifelse(County == "Broomfield" & State == "CO", "Denver",    
    ifelse(County == "Elbert" & State == "CO", "Denver",
    ifelse(County == "Park" & State == "CO", "Denver",
    ifelse(County == "Clear Creek" & State == "CO", "Denver",
    ifelse(County == "Alameda" & State == "CA", "Bay Area",
    ifelse(County == "Contra Costa" & State == "CA", "Bay Area",
    ifelse(County == "Marin" & State == "CA", "Bay Area",
    ifelse(County == "Monterey" & State == "CA", "Bay Area",
    ifelse(County == "Napa" & State == "CA", "Bay Area",
    ifelse(County == "San Benito" & State == "CA", "Bay Area",
    ifelse(County == "San Francisco" & State == "CA", "Bay Area",
    ifelse(County == "San Mateo" & State == "CA", "Bay Area",
    ifelse(County == "Santa Clara" & State == "CA", "Bay Area",
    ifelse(County == "Santa Cruz" & State == "CA", "Bay Area",
    ifelse(County == "Solano" & State == "CA", "Bay Area",
    ifelse(County == "Sonoma" & State == "CA", "Bay Area",
           NA)))))))))))))))))))))))))))))))))))))))))) %>%
  mutate(region = 
    ifelse(County == "Kings" & State == "NY", "New York",
    ifelse(County == "Queens" & State == "NY", "New York",
    ifelse(County == "New York" & State == "NY", "New York",
    ifelse(County == "Bronx" & State == "NY", "New York",
    ifelse(County == "Richmond" & State == "NY", "New York",
    ifelse(County == "Westchester" & State == "NY", "New York",
    ifelse(County == "Bergen" & State == "NY", "New York",
    ifelse(County == "Hudson" & State == "NY", "New York",
    ifelse(County == "Passaic" & State == "NY", "New York",
    ifelse(County == "Putnam" & State == "NY", "New York",
    ifelse(County == "Rockland" & State == "NY", "New York",
    ifelse(County == "Suffolk" & State == "NY", "New York",
    ifelse(County == "Nassau" & State == "NY", "New York",
    ifelse(County == "Middlesex" & State == "NJ", "New York",
    ifelse(County == "Monmouth" & State == "NJ", "New York",
    ifelse(County == "Ocean" & State == "NJ", "New York",
    ifelse(County == "Somerset" & State == "NJ", "New York",
    ifelse(County == "Essex" & State == "NJ", "New York",
    ifelse(County == "Union" & State == "NJ", "New York",
    ifelse(County == "Morris" & State == "NJ", "New York",
    ifelse(County == "Sussex" & State == "NJ", "New York",
    ifelse(County == "Hunterdon" & State == "NJ", "New York",
    ifelse(County == "Pike" & State == "NJ", "New York",
    region)))))))))))))))))))))))) %>%

# code nor cal region as all others in CA not already defined
  mutate(region = 
    ifelse(State == "CA" & is.na(region) == TRUE, "Nor Cal", region))

#Initial Removal of Columns that provide no benefit 

dataclean <- subset(dataclean,select = -c(Assignment.Number
                                                    ,Assignment.has.Historical.Mngr
                                                    ,Suffix
                                                    ,Assignment.Date
                                                    ,Assignment.Manager
                                                    ,Assignment.Role
                                                    ,Assignment.Title
                                                    ,Assignment.Status
                                                    ,Strategy
                                                    ,Progress.Level
                                                    ,Assignment.Group
                                                    ,Assignment.Category
                                                    ,Funding.Method
                                                        ,Expected.Book.Date
                                                        ,Qualification.Amount
                                                        ,Expected.Book.Amount
                                                        ,Expected.Book.Date
                                                        ,Hard.Gift.Total
                                                        ,Soft.Credit.Total
                                                        ,Total.Assignment.Gifts
                                                        ,No.of.Pledges
                                                        ,Proposal..
                                                        ,Proposal.Notes
                                                        ,HH.Life.Spouse.Credit
                                                        ,Last.Contact.By.Manager
                                                        ,X..of.Contacts.By.Manager
                                                        ,DonorSearch.Range
                                                        ,iWave.Range
                                                        ,WealthEngine.Range
                                                        ,Philanthropic.Commitments
                                                        ))
#cleaning up zip codes removing -4 after 
dataclean$Zip <- gsub(dataclean$Zip, pattern="-.*", replacement = "")

#adding zip code data and column 
zip <- read.csv(here::here("final_project", "Salary_Zipcode.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")


#adding zip salary column
dataclean <-dataclean %>%
    mutate(zipcode_slry = VLOOKUP(Zip, zip, NAME, S1902_C03_002E)) %>%
#slry range 
  mutate(zipslry_range = 
    ifelse(zipcode_slry %in% 10000:89999, "90K-99K",
    ifelse(zipcode_slry %in% 90000:99999, "90K-99K",
    ifelse(zipcode_slry %in% 100000:149999, "100K-149K", 
    ifelse(zipcode_slry %in% 150000:199999, "150K-199K",
    ifelse(zipcode_slry %in% 200000:249999, "200K-249K",
    ifelse(zipcode_slry %in% 250000:299999, "250K-299K",
    ifelse(zipcode_slry %in% 300000:349999, "300K-349K",
    ifelse(zipcode_slry %in% 350000:399999, "350K-399K",
    ifelse(zipcode_slry %in% 400000:499999, "400K-499K",
    ifelse(zipcode_slry %in% 500000:999999, "500K-999K",
    NA)))))))))))

sum(is.na(dataclean$zipcode_slry))
[1] 62347
#adding scholarship data (y/n)
schlr <- read.csv(here::here("final_project", "scholarship.csv"),
                 stringsAsFactors = FALSE,
                 strip.white = TRUE,
                 na.strings = "")

#adding scholarship column
dataclean <-dataclean %>%
    mutate(scholarship = VLOOKUP(ID, schlr, ID, SCHOLARSHIP)) 

#replacing NA with 0 
 dataclean$scholarship <- replace_na(dataclean$scholarship,'0')
 
#replacing Y with 1 
dataclean$scholarship<-ifelse(dataclean$scholarship=="Y",1,0)

#checking how many are N
table(dataclean$scholarship)

     0      1 
295264  27962 
#checking and deleting scholarship column 
class(dataclean$schlr_fct)
[1] "NULL"
dataclean = subset(dataclean, select = -c(scholarship))
  
#checking for duplicates N >1 indicates a records values are in the file twice 
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))

#removing duplicated records
dataclean <- unique(dataclean)

#Verifying n = 1 no ID with multiple records cleaned of dupes
dataclean %>% group_by(ID) %>% count() %>% arrange(desc(n))
NA

1d Creating many many factor variables


dataclean <- 
  dataclean %>% 
  #SEX
  mutate(sex_fct = 
           fct_explicit_na(Sex),
sex_simple = 
    fct_lump_n(Sex, n = 4),
#MARRIED
married_fct = 
           fct_explicit_na(Married),
  #DONOR SEGMENT
  donorseg_fct = 
           fct_explicit_na(Donor.Segment),
         donorseg_simple = 
           fct_lump_n(Donor.Segment, n = 4),
  #CONTACT RULE
         contact_fct = 
           fct_explicit_na(Contact.Rules),
         contact_simple = 
           fct_lump_n(Contact.Rules, n = 4),
  #SPOUSE MAIL
         spomail_fct = 
           fct_explicit_na(Spouse.Mail.Rules),
         spomail_simple = 
           fct_lump_n(Spouse.Mail.Rules, n = 4),
  #JOB TITLE
         jobtitle_fct = 
           fct_explicit_na(Job.Title),
         jobtitle_simple = 
           fct_lump_n(Job.Title, n = 5),
  #DEGREE TYPE 1
         deg1_fct = 
           fct_explicit_na(Degree.Type.1),
         deg1_simple = 
           fct_lump_n(Degree.Type.1, n = 5),
  #DEGREE TYPE 2
         deg2_fct = 
           fct_explicit_na(Degree.Type.2),
         deg2_simple = 
           fct_lump_n(Degree.Type.2, n = 5),
  #MAJOR 1
         maj1_fct = 
           fct_explicit_na(Major.1),
         maj1_simple = 
           fct_lump_n(Major.1, n = 5),
  #MAJOR 2
         maj2_fct = 
           fct_explicit_na(Major.2),
         maj2_simple = 
           fct_lump_n(Major.2, n = 5),
  #MINOR 1
         min1_fct = 
           fct_explicit_na(Minor.1),
         min1_simple = 
           fct_lump_n(Minor.1, n = 5),
  #MINOR 2
         min2_fct = 
           fct_explicit_na(Minor.2),
         min2_simple = 
           fct_lump_n(Minor.2, n = 5),
  #SCHOOL 1
         school1_fct = 
           fct_explicit_na(School.1),
         school1_simple = 
           fct_lump_n(School.1, n = 5),
  #SCHOOL 2
         school2_fct = 
           fct_explicit_na(School.2),
         school2_simple = 
           fct_lump_n(School.2, n = 5),
  #INSTITUTION TYPE
         insttype_fct = 
           fct_explicit_na(Institution.Type),
         insttype_simple = 
           fct_lump_n(Institution.Type, n = 5),
  #EXTRACURRICULAR
         extra_fct = 
           fct_explicit_na(Extracurricular),
         extra_simple = 
           fct_lump_n(Extracurricular, n = 5),
  #HH FIRST GIFT FUND
         hhfirstgift_fct = 
           fct_explicit_na(HH.First.Gift.Fund),
         hhfirstgift_simple = 
           fct_lump_n(HH.First.Gift.Fund, n = 5),
#CHILD 1 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.1.Enroll.Status),
         ch1_enroll_simple = 
           fct_lump_n(Child.1.Enroll.Status, n = 4),
#CHILD 1 MAJOR
         ch1_maj_fct = 
           fct_explicit_na(Child.1.Major),
         ch1_maj_simple = 
           fct_lump_n(Child.1.Major, n = 4),
#CHILD 1 MINOR
         ch1_min_fct = 
           fct_explicit_na(Child.1.Minor),
         ch1_min_simple = 
           fct_lump_n(Child.1.Minor, n = 4),
#CHILD 1 SCHOOL
         ch1_school_fct = 
           fct_explicit_na(Child.1.School),
         ch1_school_simple = 
           fct_lump_n(Child.1.School, n = 4),
#CHILD 1 FEEDER
         ch1_feeder_fct = 
           fct_explicit_na(Child.1.Feeder.School),
         ch1_feeder_simple = 
           fct_lump_n(Child.1.Feeder.School, n = 4),
#CHILD 2 ENROLL STATUS
         ch1_enroll_fct = 
           fct_explicit_na(Child.2.Enroll.Status),
         ch2_enroll_simple = 
           fct_lump_n(Child.2.Enroll.Status, n = 4),
#CHILD 2 MAJOR
         ch2_maj_fct = 
           fct_explicit_na(Child.2.Major),
         ch2_maj_simple = 
           fct_lump_n(Child.2.Major, n = 4),
#CHILD 2 MINOR
         ch2_min_fct = 
           fct_explicit_na(Child.2.Minor),
         ch2_min_simple = 
           fct_lump_n(Child.2.Minor, n = 4),
#CHILD 2 SCHOOL
         ch2_school_fct = 
           fct_explicit_na(Child.2.School),
         ch2_school_simple = 
           fct_lump_n(Child.2.School, n = 4),
#CHILD 2 FEEDER
         ch2_feeder_fct = 
           fct_explicit_na(Child.2.Feeder.School),
         ch2_feeder_simple = 
           fct_lump_n(Child.2.Feeder.School, n = 4),
    )



#checking to see if its a factor
#class(dataclean$sex_fct)
#class(dataclean$donorseg_fct)
#class(dataclean$contact_fct)
#class(dataclean$spomail_fct)

#checking levels
#levels(dataclean$sex_simple)
#levels(dataclean$donorseg_simple)
#levels(dataclean$contact_simple)
#levels(dataclean$spomail_simple)
#levels(dataclean$hhfirstgift_simple)

#creating a table against Sex column 
#table(dataclean$sex_fct, dataclean$sex_simple)

Region Analysis

#grouping by region and analyzing 
dataclean %>%
  group_by(region) %>%
  summarise(Count = length(region),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  mutate(mean_total_giv = dollar(mean_total_giv)) %>%
  kable(col.names = c("Region", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Region Count Mean HH Lifetime Giving
So Cal 145139 $5,090.84
NA 130306 $2,040.98
Bay Area 20641 $755.92
Nor Cal 10707 $3,823.63
Seattle 5425 $922.08
New York 4959 $1,978.49
Portland 2976 $1,098.24
Denver 2847 $257.29
NA
NA

DonorSegment Analysis

#grouping by donorsegment and analyzing 
dataclean %>%
  group_by(Donor.Segment) %>%
  summarise(Count = length(Donor.Segment),
            mean_total_giv = mean(HH.Lifetime.Giving)) %>%
  arrange(-Count) %>%
  filter(Count >= 100) %>%
  #added scales package to have the values show in dollar 
  mutate(mean_total_giv = (dollar(mean_total_giv))) %>%
  kable(col.names = c("Donor Segment", "Count", "Mean HH Lifetime Giving"), align=rep('c', 3)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Donor Segment Count Mean HH Lifetime Giving
NA 231974 $0.00
Lost Donor 69718 $4,954.47
Lapsed Donor 11193 $10,069.79
Current Donor 5603 $90,638.32
Lapsing Donor 3862 $16,590.15
At-Risk Donor 650 $77,143.93
NA
NA

First gift size

aq <- quantile(dataclean$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

aq <- as.data.frame(aq)

aq$aq <- dollar(aq$aq)

aq %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
Quantile
25% $0.00
50% $0.00
75% $0.00
90% $40.00
99% $1,910.06
NA
NA

Consecutive giving

#consecutive years of giving 
dataclean %>%
  filter(Max.Consec.Fiscal.Years > 0) %>%
  ggplot(aes(Max.Consec.Fiscal.Years)) + geom_histogram(fill = "#002845", bins = 20) + 
  theme_economist_white() +
  ggtitle("Consecutive Years of Giving Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,2)) +
  scale_y_continuous(breaks = seq(0,10000000,5000)) 

NA
NA
NA

Lifetime giving based on number of children

dataclean %>%
  filter(HH.Lifetime.Giving <= 10000) %>%
  filter(HH.Lifetime.Giving > 0) %>%
  mutate(`No_of_Children` = as.factor(`No_of_Children`)) %>%
  ggplot(aes(HH.Lifetime.Giving, fill = `No_of_Children`)) + geom_histogram(bins = 30) + theme_economist_white() +
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,100000,1000)) +
  scale_y_continuous(breaks = seq(0,100000000,5000)) +
  ggtitle("Giving distribution and number of children")+ 
  scale_fill_manual(values=c("#002845", "#00cfcc", "#ff9973"))

NA
NA
NA

Mean, Median, and Count of Giving in Age Ranges


age_range_giving <- dataclean %>%
  group_by(age_range) %>%
  summarise(avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
            med_giving = median(HH.Lifetime.Giving, na.rm = TRUE),
            amount_of_people_in_age_range = n())


glimpse(age_range_giving)
Rows: 10
Columns: 4
$ age_range                     <chr> "10 < 20 years old", "20 < 30 …
$ avg_giving                    <dbl> 0.4464244, 28.2686916, 386.687…
$ med_giving                    <dbl> 0, 0, 0, 0, 0, 0, 0, 10, 15, 0
$ amount_of_people_in_age_range <int> 3977, 24556, 21022, 16834, 207…

Part 2 - Summary Statistics

2a) Plotting average giving by age range


age_range_giving <-
  age_range_giving %>%
  mutate(age_range = factor(age_range))

ggplot(age_range_giving, aes(age_range, avg_giving)) +
  geom_bar(stat = "identity")+
  theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + labs(x = "Age Range", y = "Average Giving") +
      ggtitle("Average Giving Compared Across Age Ranges")

NA
NA

2b) Count of donors based on age range (another way to look at it)


ggplot(dataclean, 
       aes(age_range)) + 
       geom_bar() + 
       theme(axis.text.x = element_text(angle=45,
                                        hjust=1)) + 
  labs(title = "Count of Age Ranges", x = "", y = "")

NA
NA

2c) Boxplot of the Age Ranges Against the Lifetime Giving Amounts with a log scale applied - the reason we applied log scale is to resolve issues with visualizations that skew towards large values in our dataset.


ggplot(dataclean, aes(age_range,HH.Lifetime.Giving,fill = age_range)) + 
  geom_boxplot(
  outlier.colour = "red") + 
  scale_y_log10() +
  theme(axis.text.x=element_text(angle=45,hjust=1))  + labs(x = "Age Range", y = "Lifetime Giving Amount") +
      ggtitle("Lifetime Giving Compared Across Age Ranges")

NA
NA

2d) Splitting by age and gender



#creating boxplots 
dataclean %>% 
  filter(Age < 100) %>% #removing the weird outliers that are over 100 
  filter(Sex %in% c("M", "F")) %>%
  ggplot(aes(Sex, Age)) + 
  geom_boxplot() + 
  theme_economist() + 
  ggtitle("Ages of Donors Based on Gender") + 
  xlab(NULL) + ylab(NULL)
  

Giving by gender


#remove NAs U X

dataclean2 <- dataclean %>%
  filter(Sex %in% c("M", "F")) 

q <- ggplot(dataclean2) 
q + stat_summary_bin(
  aes(y = HH.Lifetime.Giving, x = Sex), 
  fun.y = "mean", geom = "bar") 
  
summary(dataclean$sex_simple)

Mean age by gender

#breakdown of sexs 
tally(group_by(dataclean, Sex))

summarize(group_by(dataclean, Sex), 
          avg_giving = mean(HH.Lifetime.Giving, na.rm = TRUE),
          avg_age = mean(Age, na.rm = TRUE),
          med_age = median(Age, na.rm = TRUE))

#grouping by sex and age range for slides 
tally(group_by(dataclean, Sex, age_range))

2e) Distribution of people in the states that they live.


  dataclean %>%
  mutate(State = ifelse(State == " ", "NA", State)) %>%
  filter(State != "NA") %>%
  group_by(State) %>%
  summarise(Count = length(State)) %>%
  filter(Count > 800) %>%
  arrange(-Count) %>%
  kable(col.names = c("Donor's State", "Count")) %>%
  kable_styling(bootstrap_options = c("condensed"),
                full_width = F)
Donor's State Count
CA 176487
WA 7957
TX 7266
NY 5659
CO 5073
AZ 4925
OR 4612
FL 4111
IL 3681
HI 3394
PA 2904
OH 2754
NV 2715
MI 2523
MA 2473
NJ 2311
VA 2158
NC 2085
GA 2044
MO 1889
MN 1732
MD 1488
TN 1443
IN 1417
CT 1380
WI 1330
UT 1173
OK 1151
AL 1120
LA 1110
ID 1096
SC 1076
KY 1032
KS 1027
NM 981
IA 880
NA
NA

2f) Looking at all donors first gift amount. 75% made a first gift of <100.


 no_non_donors <- dataclean %>%
  filter(Lifetime.Giving != 0)
  
nd <- quantile(no_non_donors$HH.First.Gift.Amount, probs = c(.25,.50,.75,.9,.99), na.rm = TRUE)

nd <- as.data.frame(nd)

nd %>%
  kable(col.names = "Quantile") %>%
  kable_styling(bootstrap_options = c("striped", "hover"),
                full_width = F)
  
  

Part 3 - Modeling

Split data and create a new set for easier analysis


#converting married Y and N to 1 and 0 
dataclean <- dataclean %>%
      mutate(Married_simple = ifelse(Married == "N",0,1))


dataclean <- dataclean %>%
  mutate(hh.lifetime.giving_fct = as.factor(HH.Lifetime.Giving)) %>%
  mutate(HH.Lifetime.Giving.Plus = log(HH.Lifetime.Giving + 1))



#Creating the velvet set - only the best can enter

datavelvet <- subset(dataclean,select = -c(
  age_range,
  Athletics,
  Birthdate,
  Category.Codes,
  Category.Descriptions,
  Child.1.Enroll.Status,
  Child.1.ID,
  Child.2.ID,
  Child.1.Major,
  Child.1.Minor,
  Child.1.School,
  Child.1.Feeder.School,
  Child.2.Enroll.Status,
  Child.2.Major,
  Child.2.Minor,
  Child.2.School,
  Child.2.Feeder.School,
  Contact.Rules,
  Degree.Type.1,
  Degree.Type.2,
  Donor.Segment,
  City,
  County,
  Conferral.Date.1,
  Conferral.Date.2,
  Contact.Rules,
  Degree.Type.1,
  Degree.Type.2,
  Extracurricular,
  HH.First.Gift.Fund,
  HH.First.Gift.Date,
  ID,
  Institution.Type,
  Job.Title,
  Last.Contact.By.Anyone,
  LegacyLeader..compass.score.,
  Married,
  Major.1,
  Major.2,
  Minor.1,
  Minor.2,
  region,
  zipcode_slry,
  Sex,
  Scholarship,
  School.1,
  School.2,
  Spouse.Birthdate,
  Spouse.Mail.Rules,
  State,
  Zip,
  zipslry_range
))

#datavelvet <-
#datavelvet[sapply(datavelvet, is.character)] <- #lapply(datavelvet[sapply(datavelvet, is.character)], 
#                                                           as.factor)

library("rsample")

data_split <- initial_split(datavelvet, prop = 0.75)

data_train <- training(data_split)
data_test <- testing(data_split)
p <- datavelvet %>%
  ggplot(aes(Age)) + geom_histogram(bins=30, fill = "blue") + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(5,100,by = 20)) +
  scale_y_continuous(breaks = seq(20,100,by = 20)) + xlim(c(20,100))
Scale for 'x' is already present. Adding another scale for 'x',
which will replace the existing scale.
ggplotly(p)
  
p


ggplot(data = datavelvet, aes(x = Age)) + geom_histogram(fill ="blue")+ xlim(c(20,100))
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

NA
NA
NA

Another Histogram


datavelvet %>%
  filter(Age >= 10) %>%
  filter(Age <= 90) %>%
  ggplot(aes(Age)) + geom_histogram(fill = "#002845", bins = 20) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,10000000,2000)) 

Age distribution by gender

#Age Gender filtered out below 15 and above 90 - also removed U X the weird values 
datavelvet %>%
  filter(Age >= 15) %>%
  filter(Age <= 90) %>%
  mutate(Sex = as.factor(Sex)) %>%
  filter(Sex != "U") %>%
  filter(Sex != "X") %>%
  ggplot(aes(Age, fill = Sex)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Age Distribution by Gender") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,10)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Donor age distribution by marital status

#Age Marital Status
datavelvet %>%
  filter(Age >= 20) %>%
  filter(Age <= 85) %>%
  ggplot(aes(Age, fill = Married)) + geom_histogram(bins = 25) + theme_economist_white() +
  ggtitle("Overall Donor Age Distribution by Marital Status") + 
  xlab(NULL) + ylab(NULL) + scale_x_continuous(breaks = seq(0,120,5)) +
  scale_y_continuous(breaks = seq(0,50000,2000)) + scale_fill_manual(values=c("#ff9973", "#00cfcc"))

Linear Model

#These will focus on predicting whether a constituent is a donor or non-donor. 


mod1lm <- lm( Lifetime.Giving ~ Married_simple,
           data = data_train)

mod2lm <- lm( Total.Giving.Years ~ Lifetime.Giving,
           data = data_train)

mod3lm <- lm( Lifetime.Giving ~ region,
           data = data_train)

summary(mod1lm)

Call:
lm(formula = Lifetime.Giving ~ Married_simple, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -2867    -2742    -2661    -2661 18111464 

Coefficients:
               Estimate Std. Error t value            Pr(>|t|)    
(Intercept)      2660.9      251.3  10.588 <0.0000000000000002 ***
Married_simple    205.9      469.1   0.439               0.661    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 104400 on 242248 degrees of freedom
Multiple R-squared:  7.953e-07, Adjusted R-squared:  -3.333e-06 
F-statistic: 0.1927 on 1 and 242248 DF,  p-value: 0.6607
summary(mod2lm)

Call:
lm(formula = Total.Giving.Years ~ Lifetime.Giving, data = data_train)

Residuals:
    Min      1Q  Median      3Q     Max 
-36.600  -0.554  -0.554  -0.554  39.403 

Coefficients:
                     Estimate    Std. Error t value
(Intercept)     0.55445026328 0.00396511550  139.83
Lifetime.Giving 0.00000343205 0.00000003795   90.43
                           Pr(>|t|)    
(Intercept)     <0.0000000000000002 ***
Lifetime.Giving <0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 1.951 on 242248 degrees of freedom
Multiple R-squared:  0.03266,   Adjusted R-squared:  0.03265 
F-statistic:  8178 on 1 and 242248 DF,  p-value: < 0.00000000000000022
summary(mod3lm)

Call:
lm(formula = Lifetime.Giving ~ region, data = data_train)

Residuals:
     Min       1Q   Median       3Q      Max 
   -3977    -3968    -3968    -3598 18110156 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)       513.0      950.9   0.539 0.589558    
regionDenver     -367.7     2739.3  -0.134 0.893220    
regionNew York   1954.2     2160.7   0.904 0.365769    
regionNor Cal    3464.0     1623.1   2.134 0.032826 *  
regionPortland    161.0     2680.2   0.060 0.952111    
regionSeattle    -128.2     2088.2  -0.061 0.951057    
regionSo Cal     3455.5     1016.1   3.401 0.000672 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 118200 on 144684 degrees of freedom
  (97559 observations deleted due to missingness)
Multiple R-squared:  0.0001214, Adjusted R-squared:  7.989e-05 
F-statistic: 2.927 on 6 and 144684 DF,  p-value: 0.007435
#increasing the giving year one year increase total giving by 0.0035


ggplot(data = data_train, aes(x = Age, y = log(HH.Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~region) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Region")
`geom_smooth()` using formula 'y ~ x'

ggplot(data = data_train, aes(x = Age, y = log(HH.Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~nmb_degree) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Number of Degrees")
`geom_smooth()` using formula 'y ~ x'

ggplot(data = data_train, aes(x = Age, y = log(HH.First.Gift.Amount))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~donorseg_fct) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("Donor Segment")
`geom_smooth()` using formula 'y ~ x'

#This plot actually has some interesting results
ggplot(data = data_train, aes(x = Age, y = log(Lifetime.Giving))) + geom_point(alpha = 1/10) + geom_smooth(method = lm) + facet_wrap(~No_of_Children) + theme_clean(base_size = 8) + labs(x = "X", y = "Y") +
      ggtitle("# Children")
`geom_smooth()` using formula 'y ~ x'

data_train %>% 
  select_if(is.factor) %>% 
  glimpse()
Rows: 242,250
Columns: 54
$ major_gifter           <fct> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ sex_fct                <fct> F, M, F, M, (Missing), M, M, (Missing…
$ sex_simple             <fct> F, M, F, M, NA, M, M, NA, M, NA, M, M…
$ married_fct            <fct> Y, Y, N, N, N, N, N, N, N, N, N, N, N…
$ donorseg_fct           <fct> Lost Donor, (Missing), (Missing), (Mi…
$ donorseg_simple        <fct> Lost Donor, NA, NA, NA, NA, Lost Dono…
$ contact_fct            <fct> No Solicitations, (Missing), (Missing…
$ contact_simple         <fct> No Solicitations, NA, NA, NA, NA, No …
$ spomail_fct            <fct> No Solicitations, (Missing), (Missing…
$ spomail_simple         <fct> No Solicitations, NA, NA, NA, NA, NA,…
$ jobtitle_fct           <fct> (Missing), Manager, (Missing), Public…
$ jobtitle_simple        <fct> NA, Other, NA, Other, NA, NA, NA, NA,…
$ deg1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ deg1_simple            <fct> NA, NA, NA, NA, NA, Bachelor of Arts,…
$ deg2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ deg2_simple            <fct> NA, NA, NA, NA, NA, Master of Arts, N…
$ maj1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ maj1_simple            <fct> NA, NA, NA, NA, NA, Other, Law (Full-…
$ maj2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ maj2_simple            <fct> NA, NA, NA, NA, NA, Other, NA, NA, NA…
$ min1_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ min1_simple            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ min2_fct               <fct> (Missing), (Missing), (Missing), (Mis…
$ min2_simple            <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ school1_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ school1_simple         <fct> NA, NA, NA, NA, NA, NA, Other, NA, NA…
$ school2_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ school2_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ insttype_fct           <fct> (Missing), (Missing), (Missing), (Mis…
$ insttype_simple        <fct> NA, NA, NA, NA, NA, NA, Law JD Full-T…
$ extra_fct              <fct> (Missing), (Missing), (Missing), (Mis…
$ extra_simple           <fct> NA, NA, NA, NA, NA, Other, NA, NA, NA…
$ hhfirstgift_fct        <fct> (Missing), (Missing), (Missing), (Mis…
$ hhfirstgift_simple     <fct> NA, NA, NA, NA, NA, Pre-SRN Conversio…
$ ch1_enroll_fct         <fct> (Missing), Program Completed, (Missin…
$ ch1_enroll_simple      <fct> NA, NA, NA, NA, NA, Program Completed…
$ ch1_maj_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_maj_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch1_min_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_min_simple         <fct> NA, NA, NA, NA, NA, Non-Degree: GR Ta…
$ ch1_school_fct         <fct> (Missing), (Missing), (Missing), (Mis…
$ ch1_school_simple      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch1_feeder_fct         <fct> (Missing), Palm Beach State College, …
$ ch1_feeder_simple      <fct> NA, Other, NA, NA, NA, NA, NA, NA, NA…
$ ch2_enroll_simple      <fct> NA, Program Completed, NA, NA, NA, NA…
$ ch2_maj_fct            <fct> (Missing), Business Administration BS…
$ ch2_maj_simple         <fct> NA, Business Administration BS, NA, N…
$ ch2_min_fct            <fct> (Missing), (Missing), (Missing), (Mis…
$ ch2_min_simple         <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ ch2_school_fct         <fct> (Missing), George L. Argyros School o…
$ ch2_school_simple      <fct> NA, George L. Argyros School of Busin…
$ ch2_feeder_fct         <fct> (Missing), (Missing), (Missing), (Mis…
$ ch2_feeder_simple      <fct> NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ hh.lifetime.giving_fct <fct> 25, 0, 0, 0, 0, 8048.75, 0, 0, 0, 0, …

MORE MODELS

Big logistic model


# Set family to binomial to set logistic function
# Run the model on the training set

donor_logit1 <-
  glm(hh.lifetime.giving_fct ~ Married_simple,
      family = "binomial",
      data = data_train)

summary(donor_logit1)


donor_logit2 <-
  glm(hh.lifetime.giving_fct ~ No_of_Children,
      family = "binomial",
      data = data_train)

summary(donor_logit2)


#summary(data_train$major_gifter)
#Assignment_flag taken out - may add back

donor_logit3 <-
  glm(major_gifter ~ Married + No_of_Children + donorseg_simple +  Total.Giving.Years + nmb_degree,
      family = "binomial",
      data = data_train)

summary(donor_logit3)

Call:
glm(formula = major_gifter ~ Married + No_of_Children + donorseg_simple + 
    Total.Giving.Years + nmb_degree, family = "binomial", data = data_train)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-3.0162  -0.1346  -0.1189  -0.0647   4.2151  

Coefficients:
                              Estimate Std. Error z value
(Intercept)                  -3.623014   0.232406 -15.589
MarriedY                     -1.196678   0.088444 -13.530
No_of_Children                0.610558   0.060093  10.160
donorseg_simpleCurrent Donor -0.086079   0.238167  -0.361
donorseg_simpleLapsed Donor  -0.685781   0.244285  -2.807
donorseg_simpleLapsing Donor -0.491440   0.259772  -1.892
donorseg_simpleLost Donor    -1.349829   0.232121  -5.815
Total.Giving.Years            0.206177   0.005601  36.809
nmb_degree                   -2.493578   0.146982 -16.965
                                         Pr(>|z|)    
(Intercept)                  < 0.0000000000000002 ***
MarriedY                     < 0.0000000000000002 ***
No_of_Children               < 0.0000000000000002 ***
donorseg_simpleCurrent Donor               0.7178    
donorseg_simpleLapsed Donor                0.0050 ** 
donorseg_simpleLapsing Donor               0.0585 .  
donorseg_simpleLost Donor           0.00000000606 ***
Total.Giving.Years           < 0.0000000000000002 ***
nmb_degree                   < 0.0000000000000002 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

(Dispersion parameter for binomial family taken to be 1)

    Null deviance: 9606.7  on 68301  degrees of freedom
Residual deviance: 7052.9  on 68293  degrees of freedom
  (173948 observations deleted due to missingness)
AIC: 7070.9

Number of Fisher Scoring iterations: 8
exp(donor_logit3$coefficients)
                 (Intercept)                     MarriedY 
                  0.02670207                   0.30219634 
              No_of_Children donorseg_simpleCurrent Donor 
                  1.84145937                   0.91752216 
 donorseg_simpleLapsed Donor donorseg_simpleLapsing Donor 
                  0.50369656                   0.61174479 
   donorseg_simpleLost Donor           Total.Giving.Years 
                  0.25928458                   1.22897028 
                  nmb_degree 
                  0.08261383 
#training predictions for in sample preds 
preds_train <- predict(donor_logit3, newdata = data_train, type = "response") 

#test predicts for OOS (out of sample)
preds_test <- predict(donor_logit3, newdata = data_test, type = "response")

head(preds_train)
     236259      260845      287321      219737       27673 
0.015426706 0.009114444          NA          NA          NA 
     298107 
         NA 
head(preds_test)
          4          10          11          14          15 
0.132366655 0.002564702 0.888420638 0.136046442 0.742195040 
         18 
0.002087869 
results_train <- data.frame(
  `truth` = data_train   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_train,
  `type` = rep("train",length(preds_train))
)

results_test <- data.frame(
  `truth` = data_test   %>% select(major_gifter) %>% 
    mutate(major_gifter = as.numeric(major_gifter)),
  `Class1` =  preds_test,
  `type` = rep("test",length(preds_test))
)

results <- bind_rows(results_train,results_test)

dim(results_train)
[1] 242250      3
dim(results_test)
[1] 80750     3
dim(results)
[1] 323000      3
library('plotROC')

p_plot <-
  ggplot(results,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 2.5,
           #Took the labelsize down to avoid cutoff
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 #We removed some of the cutoffs to avoid the mashup near the origin.

  #Changed the theme to avoid cutoff plot values.
  theme_classic(base_size = 14) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")
print(p_plot) 



p_train <-
  ggplot(results_train,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

p_test <-
  ggplot(results_test,
         aes(m = Class1, d = major_gifter, color = type)) +
  geom_roc(labelsize = 3.5,
           cutoffs.at = c(0.7,0.5,0.3,0.1,0)) +
 
  theme_minimal(base_size = 16) + 
  labs(x = "False Positive Rate", 
       y = "True Positive Rate") +
      ggtitle("ROC Plot: Training and Test")

#summary(donor_logit3)
#coef(donor_logit3)


#Calculating AUC of both
print(calc_auc(p_train)$AUC)
[1] 0.8918463
print(calc_auc(p_test)$AUC)
[1] 0.8839279

RIDGE


library('glmnet')
library('glmnetUtils')

ridge_fit1 <- cv.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + donorseg_fct + No_of_Children,
                       data = data_train,
                       alpha = 0)

#Alpha 0 sets the Ridge
print(ridge_fit1)
Call:
cv.glmnet.formula(formula = HH.Lifetime.Giving.Plus ~ sex_fct + 
    donorseg_fct + No_of_Children, data = data_train, alpha = 0)

Model fitting options:
    Sparse model matrix: FALSE
    Use model.frame: FALSE
    Number of crossvalidation folds: 10
    Alpha: 0
    Deviance-minimizing lambda: 0.2204993  (+1 SE): 0.2914871
print(ridge_fit1$lambda.min)
[1] 0.2204993
print(ridge_fit1$lambda.1se)
[1] 0.2914871
plot(ridge_fit1)

BIG RIDGE


library('glmnet')
library('glmnetUtils')

data_train %>% map(levels) %>% map(length)

ridge_fit2 <- cv.glmnet(HH.Lifetime.Giving.Plus ~ .,
                       data = data_train,
                       alpha = 0)

#Alpha 0 sets the Ridge
print(ridge_fit2)
print(ridge_fit2$lambda.min)

print(ridge_fit2$lambda.1se)
plot(ridge_fit2)

LASSO


#Using cv.glmnet from class
#ls(data_train) 
#is.factor(data_train$major_gifter)
#glimpse(data_train$Lifetime.Giving)

#data_train %>% 
#  select_if(is.factor) %>% 
#  glimpse()



library(glmnet)
library(glmnetUtils)
lasso_fit <- cv.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + jobtitle_simple + nmb_degree + school1_simple + hhfirstgift_simple + maj1_simple + donorseg_simple + No_of_Children + Married,
                       data = data_train,
                       #Alpha 1 for lasso
                       alpha = 1)


print(lasso_fit$lambda.min)
#
print(lasso_fit$lambda.1se)

plot(lasso_fit)

coef(lasso_fit)
#Default setting is lambda.1se

#From the book - showing convergence with lambda values
plot(lasso_fit$glmnet.fit, xvar="lambda")
#abline(v=log(c(lasso_fit$lambda.min, lasso_fit$lambda.1se)), lty=2)

enet_mod <- cva.glmnet(HH.Lifetime.Giving.Plus ~ sex_fct + jobtitle_simple + nmb_degree + school1_simple + hhfirstgift_simple + maj1_simple + donorseg_simple + No_of_Children + Married,
                       data = data_train,
                       alpha = seq(0,1, by = 0.1))

print(enet_mod)
plot(enet_mod)

ELASTICNET


minlossplot(enet_mod, 
            cv.type = "min")

get_alpha <- function(fit) {
  alpha <- fit$alpha
  error <- sapply(fit$modlist, 
                  function(mod) {min(mod$cvm)})
  alpha[which.min(error)]
}

get_model_params <- function(fit) {
  alpha <- fit$alpha
  lambdaMin <- sapply(fit$modlist, `[[`, "lambda.min")
  lambdaSE <- sapply(fit$modlist, `[[`, "lambda.1se")
  error <- sapply(fit$modlist, function(mod) {min(mod$cvm)})
  best <- which.min(error)
  data.frame(alpha = alpha[best], lambdaMin = lambdaMin[best],
             lambdaSE = lambdaSE[best], eror = error[best])
}

best_alpha <- get_alpha(enet_mod)
print(best_alpha)
get_model_params(enet_mod)

best_mod <- enet_mod$modlist[[which(enet_mod$alpha == best_alpha)]]

print(best_mod)

minlossplot(enet_mod, cv.type = "min")

Ridges plot - could be useful for plotting donations vs donor segment


library('ggridges')

summary(data_train$variable)

ggplot(data_train, aes(x = HH.Lifetime.Giving, y = region)) + geom_density_ridges(rel_min_height = 0.005) + xlim(c(25000, 100000)) + 
      ggtitle("HH Lifetime Giving by Region")

library('corrplot')

#removing ID zip and nonnumeric 
corrplot_data <- dataclean[-c(1:48,52:56,58:60,63,66:67,70:72,74:81,83:132)]

#Convert from character to numeric data type
convert_fac2num <- function(x){
  as.numeric(as.factor(x))
}

corrplot_data <- mutate_at(corrplot_data,
                     .vars = c(1:12),
                     .funs = convert_fac2num)
#making a matrix
cd_cor <- cor(corrplot_data)

#creating correlation
col <- colorRampPalette(c("#BB4400", "#EE9990", 
  "#FFFFFF", "#77AAEE", "#4477BB"))
corrplot(cd_cor, method="color", col=col(100),
  type="lower", addCoef.col = "black",
  tl.pos="lt", tl.col="black", 
  tl.cex=0.7, tl.srt=45, 
  number.cex=0.7,
  diag=FALSE)

#correlation matrix
# pairs(~Age + Months.Since.Last.Gift + donorseg_fct + 
#     nmb_degree + No_of_Children + HH.First.Gift.Age + HH.First.Gift.Amount + Total.Giving.Years,
#     col = corrplot_data$HH.Lifetime.Giving,
#     data = corrplot_data, 
#     main = "Donor Scatter Plot Matrix")

#worthless.. 

ggplot(data = corrplot_data, aes(x = nmb_degree, y = HH.Lifetime.Giving)) + 
  geom_point(aplha = 1/10)+
  geom_smooth(method = "lm", color ="red") 

Random Forest


library('randomForest')

rf_fit_donor <- randomForest(Lifetime.Giving ~ ., 
                       data = data_train,
                       type = classification,
                       mtry = 7,
                       na.action = na.roughfix,
                       ntree = 200,
                       importance=TRUE
                       )

print(rf_fit_donor)

varImpPlot(rf_fit_donor, sort = TRUE, 
           n.var = 5,
           type = 2, class = NULL, scale = TRUE, 
           main = deparse(substitute(rf_fit_donor)))

library('randomForestExplainer')

plot_min_depth_distribution(
  rf_fit_donor,
  k = 10,
  min_no_of_trees = 0,
  mean_sample = "top_trees",
  mean_scale = FALSE,
  mean_round = 2,
  main = "Distribution of minimal depth and its mean"
)
#Splitting Category out to check if the category is useful for analysis
data_category_split_out <- dataclean %>%
  mutate(Category.Codes = trim(strsplit(as.character(Category.Codes), "|", fixed = TRUE))) %>%
  unnest(Category.Codes) %>% pivot_wider(names_from = Category.Codes,values_from =Category.Codes, values_fn = length)
LS0tCnRpdGxlOiAiQlJPQ09ERSBTdW1tYXJ5IFN0YXRpc3RpY3MiCmF1dGhvcjogIkFhcm9uIFdpbGxpcywgQ2Fubm9uIEJyb29rZSwgSm9zaHVhIEhlbmRlcnNvbiwgUnlhbiBSYWRjbGlmZiIKc3VidGl0bGU6ICJCVVM2OTYgRmluYWwgUHJvamVjdCB2MTUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQoKIyBQbGVhc2UgbGVhdmUgdGhpcyBjb2RlIGNodW5rIGFzIGlzLiBJdCBtYWtlcyBzb21lIHNsaWdodCBmb3JtYXR0aW5nIGNoYW5nZXMgdG8gYWx0ZXIgdGhlIG91dHB1dCB0byBiZSBtb3JlIGFlc3RoZXRpY2FsbHkgcGxlYXNpbmcuIAoKbGlicmFyeSgna25pdHInKQoKCiMgQ2hhbmdlIHRoZSBudW1iZXIgaW4gc2V0IHNlZWQgdG8geW91ciBvd24gZmF2b3JpdGUgbnVtYmVyCnNldC5zZWVkKDE4MTgpCm9wdGlvbnMod2lkdGg9NzApCm9wdGlvbnMoc2NpcGVuPTk5KQoKCiMgdGhpcyBzZXRzIHRleHQgb3V0cHV0dGVkIGluIGNvZGUgY2h1bmtzIHRvIHNtYWxsCm9wdHNfY2h1bmskc2V0KHRpZHkub3B0cz1saXN0KHdpZHRoLndyYXA9NTApLHRpZHk9VFJVRSwgc2l6ZSA9ICJ2c21hbGwiKSAgCm9wdHNfY2h1bmskc2V0KG1lc3NhZ2UgPSBGQUxTRSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLAogICAgICAgICAgICAgICAjICJjYWNoaW5nIiBzdG9yZXMgb2JqZWN0cyBpbiBjb2RlIGNodW5rcyBhbmQgb25seSByZXdyaXRlcyBpZiB5b3UgY2hhbmdlIHRoaW5ncwogICAgICAgICAgICAgICBjYWNoZSA9IFRSVUUsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAjIGF1dG9tYXRpY2FsbHkgZG93bmxvYWRzIGRlcGVuZGVuY3kgZmlsZXMKICAgICAgICAgICAgICAgYXV0b2RlcCA9IFRSVUUsCiAgICAgICAgICAgICAgICMgCiAgICAgICAgICAgICAgIGNhY2hlLmNvbW1lbnRzID0gRkFMU0UsCiAgICAgICAgICAgICAgICMgCiAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSwKICAgICAgICAgICAgICAgIyBjaGFuZ2UgZmlnLndpZHRoIGFuZCBmaWcuaGVpZ2h0IHRvIGNoYW5nZSB0aGUgY29kZSBoZWlnaHQgYW5kIHdpZHRoIGJ5IGRlZmF1bHQKICAgICAgICAgICAgICAgZmlnLndpZHRoID0gNS41LCAgCiAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA0LjUsCiAgICAgICAgICAgICAgIGZpZy5hbGlnbj0nY2VudGVyJykKCgpgYGAKCmBgYHtyIHNldHVwLTJ9CgojIEFsd2F5cyBwcmludCB0aGlzIG91dCBiZWZvcmUgeW91ciBhc3NpZ25tZW50CnNlc3Npb25JbmZvKCkKZ2V0d2QoKQoKYGBgCgoKPCEtLSAjIyMgc3RhcnQgYW5zd2VyaW5nIHlvdXIgcHJvYmxlbSBzZXQgaGVyZSAtLT4KPCEtLSBZb3UgbWF5IGV4cG9ydCB5b3VyIGhvbWV3b3JrIGluIGVpdGhlciBodG1sIG9yIHBkZiwgd2l0aCB0aGUgZm9ybWVyIHVzdWFsbHkgYmVpbmcgZWFzaWVyLiAKICAgICBUbyBleHBvcnQgb3IgY29tcGlsZSB5b3VyIFJtZCBmaWxlOiBjbGljayBhYm92ZSBvbiAnS25pdCcgdGhlbiAnS25pdCB0byBIVE1MJyAtLT4KPCEtLSBCZSBzdXJlIHRvIHN1Ym1pdCBib3RoIHlvdXIgLlJtZCBmaWxlIGFuZCB0aGUgY29tcGlsZWQgLmh0bWwgb3IgLnBkZiBmaWxlIGZvciBmdWxsIGNyZWRpdCAtLT4KCgpgYGB7ciBzZXR1cC0zfQoKIyBsb2FkIGFsbCB5b3VyIGxpYnJhcmllcyBpbiB0aGlzIGNodW5rIApsaWJyYXJ5KCd0aWR5dmVyc2UnKQpsaWJyYXJ5KCJmcyIpCmxpYnJhcnkoJ2hlcmUnKQpsaWJyYXJ5KCdkcGx5cicpCmxpYnJhcnkoJ3RpZHl2ZXJzZScpCmxpYnJhcnkoJ2dncGxvdDInKQpsaWJyYXJ5KCdnZ3JlcGVsJykKbGlicmFyeSgnZ2d0aGVtZXMnKQpsaWJyYXJ5KCdmb3JjYXRzJykKbGlicmFyeSgncnNhbXBsZScpCmxpYnJhcnkoJ2x1YnJpZGF0ZScpCmxpYnJhcnkoJ2dndGhlbWVzJykKbGlicmFyeSgna2FibGVFeHRyYScpCmxpYnJhcnkoJ3Bhc3RlY3MnKQpsaWJyYXJ5KCd2aXJpZGlzJykKbGlicmFyeSgncGxvdGx5JykKbGlicmFyeSgndGlkeXF1YW50JykKbGlicmFyeSgnc2NhbGVzJykKbGlicmFyeSgiZ2RhdGEiKQoKIyBub3RlLCBkbyBub3QgcnVuIGluc3RhbGwucGFja2FnZXMoKSBpbnNpZGUgYSBjb2RlIGNodW5rLiBpbnN0YWxsIHRoZW0gaW4gdGhlIGNvbnNvbGUgb3V0c2lkZSBvZiBhIGNvZGUgY2h1bmsuIAoKYGBgCgoKCiMjIFBhcnQgMSAtIEZpbmFsIFByb2plY3QgQ2xlYW5pbmcgYW5kIFN1bW1hcnkgU3RhdGlzdGljcyAKCjFhKSBMb2FkaW5nIGRhdGEKCmBgYHtyfQoKI1JlYWRpbmcgdGhlIGRhdGEgaW4gYW5kIGRvaW5nIG1pbm9yIGluaXRpYWwgY2xlYW5pbmcgaW4gdGhlIGZ1bmN0aW9uIGNhbGwKI1JlcHJvZHVjaWJsZSBkYXRhIGFuYWx5c2lzIHNob3VsZCBhdm9pZCBhbGwgYXV0b21hdGljIHN0cmluZyB0byBmYWN0b3IgY29udmVyc2lvbnMuCiNzdHJpcC53aGl0ZSByZW1vdmVzIHdoaXRlIHNwYWNlIAojbmEuc3RyaW5ncyBpcyBhIHN1YnN0aXR1dGlvbiBzbyBhbGwgdGhhdCBoYXZlICIiIHdpbGwgPSBuYQpkYXRhIDwtIHJlYWQuY3N2KGhlcmU6OmhlcmUoImZpbmFsX3Byb2plY3QiLCAiZG9ub3JfZGF0YS5jc3YiKSwKICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgc3RyaXAud2hpdGUgPSBUUlVFLAogICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSAiIikKCgoKYGBgCgoKMWIpIEZpeGluZyB0aGUgd29ua3kgRE9CICYgRGF0YSBjbGVhbnVwCgpgYGB7cn0KCiMoQmlydGhkYXRlIGFuZCBBZ2UsIElEIGFzIGEgbnVtYmVyKWFkZGluZyBET0IgKEFnZS9TcG91c2UgQWdlKSBpbiB5ZWFycyBjb2x1bW5zIGFuZCBhZGRpbmcgdHdvIGZpZWxkcyBmb3IgYXNzaWdubWVudCBhbmQgbnVtYmVyIG9mIGNoaWxkcmVuIGFuZCBudW1iZXIgb2YgZGVncmVlcwpkYXRhY2xlYW4gPC0gZGF0YSAlPiUKICBtdXRhdGUoQmlydGhkYXRlID0gaWZlbHNlKEJpcnRoZGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBCaXJ0aGRhdGUpKSAlPiUKICBtdXRhdGUoQmlydGhkYXRlID0gbWR5KEJpcnRoZGF0ZSkpICU+JQogIG11dGF0ZShBZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBCaXJ0aGRhdGUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgbXV0YXRlKFNwb3VzZS5CaXJ0aGRhdGUgPSBpZmVsc2UoU3BvdXNlLkJpcnRoZGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBTcG91c2UuQmlydGhkYXRlKSkgJT4lCiAgbXV0YXRlKFNwb3VzZS5CaXJ0aGRhdGUgPSBtZHkoU3BvdXNlLkJpcnRoZGF0ZSkpICU+JQogIG11dGF0ZShTcG91c2UuQWdlID0gYXMubnVtZXJpYyhmbG9vcihpbnRlcnZhbChzdGFydD0gU3BvdXNlLkJpcnRoZGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZW5kPVN5cy5EYXRlKCkpL2R1cmF0aW9uKG49MSwgdW5pdD0ieWVhcnMiKSkpKSAlPiUKICBtdXRhdGUoSUQgPSBhcy5udW1lcmljKElEKSkgJT4lIAogIG11dGF0ZShBc3NpZ25tZW50X2ZsYWcgPSBpZmVsc2UoaXMubmEoQXNzaWdubWVudC5OdW1iZXIpLCAwLDEpKSAlPiUgCiAgbXV0YXRlKCBOb19vZl9DaGlsZHJlbiA9IGlmZWxzZShpcy5uYShDaGlsZC4xLklEKSwwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKENoaWxkLjIuSUQpLDEsMikpKSU+JQogbXV0YXRlKElEID0gYXMubnVtZXJpYyhJRCkpICU+JSAKICAgIG11dGF0ZSggbm1iX2RlZ3JlZSA9IGlmZWxzZShpcy5uYShEZWdyZWUuVHlwZS4xKSwwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGlzLm5hKERlZ3JlZS5UeXBlLjIpLDEsMikpKSAlPiUKI2NvbmZlcnJhbCBkYXRlcwogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4xID0gaWZlbHNlKENvbmZlcnJhbC5EYXRlLjEgPT0gIjAwMDEtMDEtMDEiLCBOQSwgQ29uZmVycmFsLkRhdGUuMSkpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4xID0gbWR5KENvbmZlcnJhbC5EYXRlLjEpKSAlPiUKICBtdXRhdGUoQ29uZmVycmFsLkRhdGUuMS5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBDb25mZXJyYWwuRGF0ZS4xLCBlbmQ9U3lzLkRhdGUoKSkvZHVyYXRpb24obj0xLCB1bml0PSJ5ZWFycyIpKSkpICU+JQogIAogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4yID0gaWZlbHNlKENvbmZlcnJhbC5EYXRlLjIgPT0gIjAwMDEtMDEtMDEiLCBOQSwgQ29uZmVycmFsLkRhdGUuMikpICU+JQogIG11dGF0ZShDb25mZXJyYWwuRGF0ZS4yID0gbWR5KENvbmZlcnJhbC5EYXRlLjIpKSAlPiUKICBtdXRhdGUoQ29uZmVycmFsLkRhdGUuMi5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBDb25mZXJyYWwuRGF0ZS4yLCBlbmQ9U3lzLkRhdGUoKSkvZHVyYXRpb24obj0xLCB1bml0PSJ5ZWFycyIpKSkpICU+JQogIAogIG11dGF0ZShMYXN0LkNvbnRhY3QuQnkuQW55b25lID0gaWZlbHNlKExhc3QuQ29udGFjdC5CeS5BbnlvbmUgPT0gIjAwMDEtMDEtMDEiLCBOQSwgTGFzdC5Db250YWN0LkJ5LkFueW9uZSkpICU+JQogIG11dGF0ZShMYXN0LkNvbnRhY3QuQnkuQW55b25lID0gbWR5KExhc3QuQ29udGFjdC5CeS5BbnlvbmUpKSAlPiUKICBtdXRhdGUoTGFzdC5Db250YWN0LkFnZSA9IGFzLm51bWVyaWMoZmxvb3IoaW50ZXJ2YWwoc3RhcnQ9IExhc3QuQ29udGFjdC5CeS5BbnlvbmUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lCiAgCiBtdXRhdGUoSEguRmlyc3QuR2lmdC5EYXRlID0gaWZlbHNlKEhILkZpcnN0LkdpZnQuRGF0ZSA9PSAiMDAwMS0wMS0wMSIsIE5BLCBISC5GaXJzdC5HaWZ0LkRhdGUpKSAlPiUKICBtdXRhdGUoSEguRmlyc3QuR2lmdC5EYXRlID0gbWR5KEhILkZpcnN0LkdpZnQuRGF0ZSkpICU+JQptdXRhdGUoSEguRmlyc3QuR2lmdC5BZ2UgPSBhcy5udW1lcmljKGZsb29yKGludGVydmFsKHN0YXJ0PSBISC5GaXJzdC5HaWZ0LkRhdGUsIGVuZD1TeXMuRGF0ZSgpKS9kdXJhdGlvbihuPTEsIHVuaXQ9InllYXJzIikpKSkgJT4lIAojbWFqb3IgZ2lmdCAKICBtdXRhdGUobWFqb3JfZ2lmdGVyID0gaWZlbHNlKExpZmV0aW1lLkdpdmluZyA+IDUwMDAwLCAxLDApICU+JSBmYWN0b3IoLiwgbGV2ZWxzID0gYygiMCIsIjEiKSkpICU+JQojc3BsaXR0aW5nIHVwIHRoZSBhZ2UgaW50byByYW5nZXMgYW5kIGNyZWF0aW5nIGNhdGVnb3J5IGZvciBlYXN5IHZpc3VhbGl6YXRpb24gCiAgbXV0YXRlKGFnZV9yYW5nZSA9IAogICAgaWZlbHNlKEFnZSAlaW4lIDEwOjE5LCAiMTAgPCAyMCB5ZWFycyBvbGQiLAogICAgaWZlbHNlKEFnZSAlaW4lIDIwOjI5LCAiMjAgPCAzMCB5ZWFycyBvbGQiLCAKICAgIGlmZWxzZShBZ2UgJWluJSAzMDozOSwgIjMwIDwgNDAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA0MDo0OSwgIjQwIDwgNTAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA1MDo1OSwgIjUwIDwgNjAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA2MDo2OSwgIjYwIDwgNzAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA3MDo3OSwgIjcwIDwgODAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA4MDo4OSwgIjgwIDwgOTAgeWVhcnMgb2xkIiwKICAgIGlmZWxzZShBZ2UgJWluJSA5MDoxMjAsICI5MCsgeWVhcnMgb2xkIiwKICAgIE5BKSkpKSkpKSkpKSAlPiUKI2NyZWF0aW5nIGEgcmVnaW9uIGNvbHVtbiB1c2luZyB0aGUgY291bnR5IGRhdGEgYW5kIHRoZSBPTUIgTVNBIChNZXRyb3BvbGl0YW4gU3RhdGlzdGljYWwgQXJlYSkgZGVmaW5pdGlvbnMKICBtdXRhdGUocmVnaW9uID0gCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW4gTHVpcyBPYmlzcG8iICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJLZXJuIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIEJlcm5hcmRpbm8iICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW50YSBCYXJiYXJhIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiVmVudHVyYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiU28gQ2FsIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkxvcyBBbmdlbGVzIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiT3JhbmdlIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUml2ZXJzaWRlIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIERpZWdvIiAmIFN0YXRlID09ICJDQSIsICJTbyBDYWwiLAogICAgaWZlbHNlKENvdW50eSA9PSAiSW1wZXJpYWwiICYgU3RhdGUgPT0gIkNBIiwgIlNvIENhbCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJLaW5nIiAmIFN0YXRlID09ICJXQSIsICJTZWF0dGxlIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNub2hvbWlzaCIgJiBTdGF0ZSA9PSAiV0EiLCAiU2VhdHRsZSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQaWVyY2UiICYgU3RhdGUgPT0gIldBIiwgIlNlYXR0bGUiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ2xhY2thbWFzIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJDb2x1bWJpYSIgJiBTdGF0ZSA9PSAiT1IiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTXVsdG5vbWFoIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJXYXNoaW5ndG9uIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJZYW1oaWxsIiAmIFN0YXRlID09ICJPUiIsICJQb3J0bGFuZCIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJDbGFyayIgJiBTdGF0ZSA9PSAiV0EiLCAiUG9ydGxhbmQiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2thbWFuaWEiICYgU3RhdGUgPT0gIldBIiwgIlBvcnRsYW5kIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkRlbnZlciIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIkFyYXBhaG9lIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiSmVmZmVyc29uIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQWRhbXMiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJEb3VnbGFzIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQnJvb21maWVsZCIgJiBTdGF0ZSA9PSAiQ08iLCAiRGVudmVyIiwgICAgCiAgICBpZmVsc2UoQ291bnR5ID09ICJFbGJlcnQiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJQYXJrIiAmIFN0YXRlID09ICJDTyIsICJEZW52ZXIiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQ2xlYXIgQ3JlZWsiICYgU3RhdGUgPT0gIkNPIiwgIkRlbnZlciIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJBbGFtZWRhIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJDb250cmEgQ29zdGEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk1hcmluIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNb250ZXJleSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTmFwYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIEJlbml0byIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIEZyYW5jaXNjbyIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FuIE1hdGVvIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTYW50YSBDbGFyYSIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU2FudGEgQ3J1eiIgJiBTdGF0ZSA9PSAiQ0EiLCAiQmF5IEFyZWEiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU29sYW5vIiAmIFN0YXRlID09ICJDQSIsICJCYXkgQXJlYSIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJTb25vbWEiICYgU3RhdGUgPT0gIkNBIiwgIkJheSBBcmVhIiwKICAgICAgICAgICBOQSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSAlPiUKICBtdXRhdGUocmVnaW9uID0gCiAgICBpZmVsc2UoQ291bnR5ID09ICJLaW5ncyIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUXVlZW5zIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJOZXcgWW9yayIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQnJvbngiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlJpY2htb25kIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJXZXN0Y2hlc3RlciIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiQmVyZ2VuIiAmIFN0YXRlID09ICJOWSIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJIdWRzb24iICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlBhc3NhaWMiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlB1dG5hbSIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiUm9ja2xhbmQiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlN1ZmZvbGsiICYgU3RhdGUgPT0gIk5ZIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk5hc3NhdSIgJiBTdGF0ZSA9PSAiTlkiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiTWlkZGxlc2V4IiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJNb25tb3V0aCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiT2NlYW4iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlNvbWVyc2V0IiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJFc3NleCIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiVW5pb24iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIk1vcnJpcyIgJiBTdGF0ZSA9PSAiTkoiLCAiTmV3IFlvcmsiLAogICAgaWZlbHNlKENvdW50eSA9PSAiU3Vzc2V4IiAmIFN0YXRlID09ICJOSiIsICJOZXcgWW9yayIsCiAgICBpZmVsc2UoQ291bnR5ID09ICJIdW50ZXJkb24iICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIGlmZWxzZShDb3VudHkgPT0gIlBpa2UiICYgU3RhdGUgPT0gIk5KIiwgIk5ldyBZb3JrIiwKICAgIHJlZ2lvbikpKSkpKSkpKSkpKSkpKSkpKSkpKSkpKSAlPiUKCiMgY29kZSBub3IgY2FsIHJlZ2lvbiBhcyBhbGwgb3RoZXJzIGluIENBIG5vdCBhbHJlYWR5IGRlZmluZWQKICBtdXRhdGUocmVnaW9uID0gCiAgICBpZmVsc2UoU3RhdGUgPT0gIkNBIiAmIGlzLm5hKHJlZ2lvbikgPT0gVFJVRSwgIk5vciBDYWwiLCByZWdpb24pKQoKI0luaXRpYWwgUmVtb3ZhbCBvZiBDb2x1bW5zIHRoYXQgcHJvdmlkZSBubyBiZW5lZml0IAoKZGF0YWNsZWFuIDwtIHN1YnNldChkYXRhY2xlYW4sc2VsZWN0ID0gLWMoQXNzaWdubWVudC5OdW1iZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50Lmhhcy5IaXN0b3JpY2FsLk1uZ3IKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTdWZmaXgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LkRhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50Lk1hbmFnZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlJvbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxBc3NpZ25tZW50LlRpdGxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5TdGF0dXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTdHJhdGVneQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLFByb2dyZXNzLkxldmVsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsQXNzaWdubWVudC5Hcm91cAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEFzc2lnbm1lbnQuQ2F0ZWdvcnkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxGdW5kaW5nLk1ldGhvZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxFeHBlY3RlZC5Cb29rLkRhdGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUXVhbGlmaWNhdGlvbi5BbW91bnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsRXhwZWN0ZWQuQm9vay5BbW91bnQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsRXhwZWN0ZWQuQm9vay5EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLEhhcmQuR2lmdC5Ub3RhbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxTb2Z0LkNyZWRpdC5Ub3RhbAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxUb3RhbC5Bc3NpZ25tZW50LkdpZnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLE5vLm9mLlBsZWRnZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUHJvcG9zYWwuLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxQcm9wb3NhbC5Ob3RlcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxISC5MaWZlLlNwb3VzZS5DcmVkaXQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsTGFzdC5Db250YWN0LkJ5Lk1hbmFnZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsWC4ub2YuQ29udGFjdHMuQnkuTWFuYWdlcgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxEb25vclNlYXJjaC5SYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxpV2F2ZS5SYW5nZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICxXZWFsdGhFbmdpbmUuUmFuZ2UKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsUGhpbGFudGhyb3BpYy5Db21taXRtZW50cwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkpCiNjbGVhbmluZyB1cCB6aXAgY29kZXMgcmVtb3ZpbmcgLTQgYWZ0ZXIgCmRhdGFjbGVhbiRaaXAgPC0gZ3N1YihkYXRhY2xlYW4kWmlwLCBwYXR0ZXJuPSItLioiLCByZXBsYWNlbWVudCA9ICIiKQoKI2FkZGluZyB6aXAgY29kZSBkYXRhIGFuZCBjb2x1bW4gCnppcCA8LSByZWFkLmNzdihoZXJlOjpoZXJlKCJmaW5hbF9wcm9qZWN0IiwgIlNhbGFyeV9aaXBjb2RlLmNzdiIpLAogICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBzdHJpcC53aGl0ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQoKCiNhZGRpbmcgemlwIHNhbGFyeSBjb2x1bW4KZGF0YWNsZWFuIDwtZGF0YWNsZWFuICU+JQogICAgbXV0YXRlKHppcGNvZGVfc2xyeSA9IFZMT09LVVAoWmlwLCB6aXAsIE5BTUUsIFMxOTAyX0MwM18wMDJFKSkgJT4lCiNzbHJ5IHJhbmdlIAogIG11dGF0ZSh6aXBzbHJ5X3JhbmdlID0gCiAgICBpZmVsc2UoemlwY29kZV9zbHJ5ICVpbiUgMTAwMDA6ODk5OTksICI5MEstOTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSA5MDAwMDo5OTk5OSwgIjkwSy05OUsiLAogICAgaWZlbHNlKHppcGNvZGVfc2xyeSAlaW4lIDEwMDAwMDoxNDk5OTksICIxMDBLLTE0OUsiLCAKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAxNTAwMDA6MTk5OTk5LCAiMTUwSy0xOTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAyMDAwMDA6MjQ5OTk5LCAiMjAwSy0yNDlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAyNTAwMDA6Mjk5OTk5LCAiMjUwSy0yOTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAzMDAwMDA6MzQ5OTk5LCAiMzAwSy0zNDlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSAzNTAwMDA6Mzk5OTk5LCAiMzUwSy0zOTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSA0MDAwMDA6NDk5OTk5LCAiNDAwSy00OTlLIiwKICAgIGlmZWxzZSh6aXBjb2RlX3NscnkgJWluJSA1MDAwMDA6OTk5OTk5LCAiNTAwSy05OTlLIiwKICAgIE5BKSkpKSkpKSkpKSkKCnN1bShpcy5uYShkYXRhY2xlYW4kemlwY29kZV9zbHJ5KSkKCiNhZGRpbmcgc2Nob2xhcnNoaXAgZGF0YSAoeS9uKQpzY2hsciA8LSByZWFkLmNzdihoZXJlOjpoZXJlKCJmaW5hbF9wcm9qZWN0IiwgInNjaG9sYXJzaGlwLmNzdiIpLAogICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBzdHJpcC53aGl0ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgbmEuc3RyaW5ncyA9ICIiKQoKI2FkZGluZyBzY2hvbGFyc2hpcCBjb2x1bW4KZGF0YWNsZWFuIDwtZGF0YWNsZWFuICU+JQogICAgbXV0YXRlKHNjaG9sYXJzaGlwID0gVkxPT0tVUChJRCwgc2NobHIsIElELCBTQ0hPTEFSU0hJUCkpIAoKI3JlcGxhY2luZyBOQSB3aXRoIDAgCiBkYXRhY2xlYW4kc2Nob2xhcnNoaXAgPC0gcmVwbGFjZV9uYShkYXRhY2xlYW4kc2Nob2xhcnNoaXAsJzAnKQogCiNyZXBsYWNpbmcgWSB3aXRoIDEgCmRhdGFjbGVhbiRzY2hvbGFyc2hpcDwtaWZlbHNlKGRhdGFjbGVhbiRzY2hvbGFyc2hpcD09IlkiLDEsMCkKCiNjaGVja2luZyBob3cgbWFueSBhcmUgTgp0YWJsZShkYXRhY2xlYW4kc2Nob2xhcnNoaXApCgoKI2NoZWNraW5nIGFuZCBkZWxldGluZyBzY2hvbGFyc2hpcCBjb2x1bW4gCmNsYXNzKGRhdGFjbGVhbiRzY2hscl9mY3QpCmRhdGFjbGVhbiA9IHN1YnNldChkYXRhY2xlYW4sIHNlbGVjdCA9IC1jKHNjaG9sYXJzaGlwKSkKICAKI2NoZWNraW5nIGZvciBkdXBsaWNhdGVzIE4gPjEgaW5kaWNhdGVzIGEgcmVjb3JkcyB2YWx1ZXMgYXJlIGluIHRoZSBmaWxlIHR3aWNlIApkYXRhY2xlYW4gJT4lIGdyb3VwX2J5KElEKSAlPiUgY291bnQoKSAlPiUgYXJyYW5nZShkZXNjKG4pKQoKI3JlbW92aW5nIGR1cGxpY2F0ZWQgcmVjb3JkcwpkYXRhY2xlYW4gPC0gdW5pcXVlKGRhdGFjbGVhbikKCiNWZXJpZnlpbmcgbiA9IDEgbm8gSUQgd2l0aCBtdWx0aXBsZSByZWNvcmRzIGNsZWFuZWQgb2YgZHVwZXMKZGF0YWNsZWFuICU+JSBncm91cF9ieShJRCkgJT4lIGNvdW50KCkgJT4lIGFycmFuZ2UoZGVzYyhuKSkKCmBgYAoKCjFkIENyZWF0aW5nIG1hbnkgbWFueSBmYWN0b3IgdmFyaWFibGVzCgpgYGB7cn0KCmRhdGFjbGVhbiA8LSAKICBkYXRhY2xlYW4gJT4lIAogICNTRVgKICBtdXRhdGUoc2V4X2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTZXgpLApzZXhfc2ltcGxlID0gCiAgICBmY3RfbHVtcF9uKFNleCwgbiA9IDQpLAojTUFSUklFRAptYXJyaWVkX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShNYXJyaWVkKSwKICAjRE9OT1IgU0VHTUVOVAogIGRvbm9yc2VnX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShEb25vci5TZWdtZW50KSwKICAgICAgICAgZG9ub3JzZWdfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihEb25vci5TZWdtZW50LCBuID0gNCksCiAgI0NPTlRBQ1QgUlVMRQogICAgICAgICBjb250YWN0X2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDb250YWN0LlJ1bGVzKSwKICAgICAgICAgY29udGFjdF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENvbnRhY3QuUnVsZXMsIG4gPSA0KSwKICAjU1BPVVNFIE1BSUwKICAgICAgICAgc3BvbWFpbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoU3BvdXNlLk1haWwuUnVsZXMpLAogICAgICAgICBzcG9tYWlsX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oU3BvdXNlLk1haWwuUnVsZXMsIG4gPSA0KSwKICAjSk9CIFRJVExFCiAgICAgICAgIGpvYnRpdGxlX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShKb2IuVGl0bGUpLAogICAgICAgICBqb2J0aXRsZV9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEpvYi5UaXRsZSwgbiA9IDUpLAogICNERUdSRUUgVFlQRSAxCiAgICAgICAgIGRlZzFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKERlZ3JlZS5UeXBlLjEpLAogICAgICAgICBkZWcxX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oRGVncmVlLlR5cGUuMSwgbiA9IDUpLAogICNERUdSRUUgVFlQRSAyCiAgICAgICAgIGRlZzJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKERlZ3JlZS5UeXBlLjIpLAogICAgICAgICBkZWcyX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oRGVncmVlLlR5cGUuMiwgbiA9IDUpLAogICNNQUpPUiAxCiAgICAgICAgIG1hajFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1ham9yLjEpLAogICAgICAgICBtYWoxX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oTWFqb3IuMSwgbiA9IDUpLAogICNNQUpPUiAyCiAgICAgICAgIG1hajJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1ham9yLjIpLAogICAgICAgICBtYWoyX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oTWFqb3IuMiwgbiA9IDUpLAogICNNSU5PUiAxCiAgICAgICAgIG1pbjFfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1pbm9yLjEpLAogICAgICAgICBtaW4xX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oTWlub3IuMSwgbiA9IDUpLAogICNNSU5PUiAyCiAgICAgICAgIG1pbjJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKE1pbm9yLjIpLAogICAgICAgICBtaW4yX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oTWlub3IuMiwgbiA9IDUpLAogICNTQ0hPT0wgMQogICAgICAgICBzY2hvb2wxX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTY2hvb2wuMSksCiAgICAgICAgIHNjaG9vbDFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihTY2hvb2wuMSwgbiA9IDUpLAogICNTQ0hPT0wgMgogICAgICAgICBzY2hvb2wyX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShTY2hvb2wuMiksCiAgICAgICAgIHNjaG9vbDJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihTY2hvb2wuMiwgbiA9IDUpLAogICNJTlNUSVRVVElPTiBUWVBFCiAgICAgICAgIGluc3R0eXBlX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShJbnN0aXR1dGlvbi5UeXBlKSwKICAgICAgICAgaW5zdHR5cGVfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihJbnN0aXR1dGlvbi5UeXBlLCBuID0gNSksCiAgI0VYVFJBQ1VSUklDVUxBUgogICAgICAgICBleHRyYV9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoRXh0cmFjdXJyaWN1bGFyKSwKICAgICAgICAgZXh0cmFfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihFeHRyYWN1cnJpY3VsYXIsIG4gPSA1KSwKICAjSEggRklSU1QgR0lGVCBGVU5ECiAgICAgICAgIGhoZmlyc3RnaWZ0X2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShISC5GaXJzdC5HaWZ0LkZ1bmQpLAogICAgICAgICBoaGZpcnN0Z2lmdF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKEhILkZpcnN0LkdpZnQuRnVuZCwgbiA9IDUpLAojQ0hJTEQgMSBFTlJPTEwgU1RBVFVTCiAgICAgICAgIGNoMV9lbnJvbGxfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjEuRW5yb2xsLlN0YXR1cyksCiAgICAgICAgIGNoMV9lbnJvbGxfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLkVucm9sbC5TdGF0dXMsIG4gPSA0KSwKI0NISUxEIDEgTUFKT1IKICAgICAgICAgY2gxX21hal9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5NYWpvciksCiAgICAgICAgIGNoMV9tYWpfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLk1ham9yLCBuID0gNCksCiNDSElMRCAxIE1JTk9SCiAgICAgICAgIGNoMV9taW5fZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjEuTWlub3IpLAogICAgICAgICBjaDFfbWluX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMS5NaW5vciwgbiA9IDQpLAojQ0hJTEQgMSBTQ0hPT0wKICAgICAgICAgY2gxX3NjaG9vbF9mY3QgPSAKICAgICAgICAgICBmY3RfZXhwbGljaXRfbmEoQ2hpbGQuMS5TY2hvb2wpLAogICAgICAgICBjaDFfc2Nob29sX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMS5TY2hvb2wsIG4gPSA0KSwKI0NISUxEIDEgRkVFREVSCiAgICAgICAgIGNoMV9mZWVkZXJfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjEuRmVlZGVyLlNjaG9vbCksCiAgICAgICAgIGNoMV9mZWVkZXJfc2ltcGxlID0gCiAgICAgICAgICAgZmN0X2x1bXBfbihDaGlsZC4xLkZlZWRlci5TY2hvb2wsIG4gPSA0KSwKI0NISUxEIDIgRU5ST0xMIFNUQVRVUwogICAgICAgICBjaDFfZW5yb2xsX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4yLkVucm9sbC5TdGF0dXMpLAogICAgICAgICBjaDJfZW5yb2xsX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5FbnJvbGwuU3RhdHVzLCBuID0gNCksCiNDSElMRCAyIE1BSk9SCiAgICAgICAgIGNoMl9tYWpfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuTWFqb3IpLAogICAgICAgICBjaDJfbWFqX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5NYWpvciwgbiA9IDQpLAojQ0hJTEQgMiBNSU5PUgogICAgICAgICBjaDJfbWluX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4yLk1pbm9yKSwKICAgICAgICAgY2gyX21pbl9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjIuTWlub3IsIG4gPSA0KSwKI0NISUxEIDIgU0NIT09MCiAgICAgICAgIGNoMl9zY2hvb2xfZmN0ID0gCiAgICAgICAgICAgZmN0X2V4cGxpY2l0X25hKENoaWxkLjIuU2Nob29sKSwKICAgICAgICAgY2gyX3NjaG9vbF9zaW1wbGUgPSAKICAgICAgICAgICBmY3RfbHVtcF9uKENoaWxkLjIuU2Nob29sLCBuID0gNCksCiNDSElMRCAyIEZFRURFUgogICAgICAgICBjaDJfZmVlZGVyX2ZjdCA9IAogICAgICAgICAgIGZjdF9leHBsaWNpdF9uYShDaGlsZC4yLkZlZWRlci5TY2hvb2wpLAogICAgICAgICBjaDJfZmVlZGVyX3NpbXBsZSA9IAogICAgICAgICAgIGZjdF9sdW1wX24oQ2hpbGQuMi5GZWVkZXIuU2Nob29sLCBuID0gNCksCiAgICApCgoKCiNjaGVja2luZyB0byBzZWUgaWYgaXRzIGEgZmFjdG9yCiNjbGFzcyhkYXRhY2xlYW4kc2V4X2ZjdCkKI2NsYXNzKGRhdGFjbGVhbiRkb25vcnNlZ19mY3QpCiNjbGFzcyhkYXRhY2xlYW4kY29udGFjdF9mY3QpCiNjbGFzcyhkYXRhY2xlYW4kc3BvbWFpbF9mY3QpCgojY2hlY2tpbmcgbGV2ZWxzCiNsZXZlbHMoZGF0YWNsZWFuJHNleF9zaW1wbGUpCiNsZXZlbHMoZGF0YWNsZWFuJGRvbm9yc2VnX3NpbXBsZSkKI2xldmVscyhkYXRhY2xlYW4kY29udGFjdF9zaW1wbGUpCiNsZXZlbHMoZGF0YWNsZWFuJHNwb21haWxfc2ltcGxlKQojbGV2ZWxzKGRhdGFjbGVhbiRoaGZpcnN0Z2lmdF9zaW1wbGUpCgojY3JlYXRpbmcgYSB0YWJsZSBhZ2FpbnN0IFNleCBjb2x1bW4gCiN0YWJsZShkYXRhY2xlYW4kc2V4X2ZjdCwgZGF0YWNsZWFuJHNleF9zaW1wbGUpCgoKYGBgCgpSZWdpb24gQW5hbHlzaXMKCmBgYHtyfQojZ3JvdXBpbmcgYnkgcmVnaW9uIGFuZCBhbmFseXppbmcgCmRhdGFjbGVhbiAlPiUKICBncm91cF9ieShyZWdpb24pICU+JQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChyZWdpb24pLAogICAgICAgICAgICBtZWFuX3RvdGFsX2dpdiA9IG1lYW4oSEguTGlmZXRpbWUuR2l2aW5nKSkgJT4lCiAgYXJyYW5nZSgtQ291bnQpICU+JQogIGZpbHRlcihDb3VudCA+PSAxMDApICU+JQogIG11dGF0ZShtZWFuX3RvdGFsX2dpdiA9IGRvbGxhcihtZWFuX3RvdGFsX2dpdikpICU+JQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIlJlZ2lvbiIsICJDb3VudCIsICJNZWFuIEhIIExpZmV0aW1lIEdpdmluZyIpLCBhbGlnbj1yZXAoJ2MnLCAzKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwKICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGKQogIAoKYGBgCgoKRG9ub3JTZWdtZW50IEFuYWx5c2lzCgpgYGB7cn0KI2dyb3VwaW5nIGJ5IGRvbm9yc2VnbWVudCBhbmQgYW5hbHl6aW5nIApkYXRhY2xlYW4gJT4lCiAgZ3JvdXBfYnkoRG9ub3IuU2VnbWVudCkgJT4lCiAgc3VtbWFyaXNlKENvdW50ID0gbGVuZ3RoKERvbm9yLlNlZ21lbnQpLAogICAgICAgICAgICBtZWFuX3RvdGFsX2dpdiA9IG1lYW4oSEguTGlmZXRpbWUuR2l2aW5nKSkgJT4lCiAgYXJyYW5nZSgtQ291bnQpICU+JQogIGZpbHRlcihDb3VudCA+PSAxMDApICU+JQogICNhZGRlZCBzY2FsZXMgcGFja2FnZSB0byBoYXZlIHRoZSB2YWx1ZXMgc2hvdyBpbiBkb2xsYXIgCiAgbXV0YXRlKG1lYW5fdG90YWxfZ2l2ID0gKGRvbGxhcihtZWFuX3RvdGFsX2dpdikpKSAlPiUKICBrYWJsZShjb2wubmFtZXMgPSBjKCJEb25vciBTZWdtZW50IiwgIkNvdW50IiwgIk1lYW4gSEggTGlmZXRpbWUgR2l2aW5nIiksIGFsaWduPXJlcCgnYycsIDMpKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCgpgYGAKCkZpcnN0IGdpZnQgc2l6ZSAKYGBge3J9CmFxIDwtIHF1YW50aWxlKGRhdGFjbGVhbiRISC5GaXJzdC5HaWZ0LkFtb3VudCwgcHJvYnMgPSBjKC4yNSwuNTAsLjc1LC45LC45OSksIG5hLnJtID0gVFJVRSkKCmFxIDwtIGFzLmRhdGEuZnJhbWUoYXEpCgphcSRhcSA8LSBkb2xsYXIoYXEkYXEpCgphcSAlPiUKICBrYWJsZShjb2wubmFtZXMgPSAiUXVhbnRpbGUiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCgpgYGAKQ29uc2VjdXRpdmUgZ2l2aW5nCmBgYHtyfQojY29uc2VjdXRpdmUgeWVhcnMgb2YgZ2l2aW5nIApkYXRhY2xlYW4gJT4lCiAgZmlsdGVyKE1heC5Db25zZWMuRmlzY2FsLlllYXJzID4gMCkgJT4lCiAgZ2dwbG90KGFlcyhNYXguQ29uc2VjLkZpc2NhbC5ZZWFycykpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICIjMDAyODQ1IiwgYmlucyA9IDIwKSArIAogIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJDb25zZWN1dGl2ZSBZZWFycyBvZiBHaXZpbmcgRGlzdHJpYnV0aW9uIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsMikpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwMDAsNTAwMCkpIAoKCgpgYGAKCkxpZmV0aW1lIGdpdmluZyBiYXNlZCBvbiBudW1iZXIgb2YgY2hpbGRyZW4gCgpgYGB7cn0KZGF0YWNsZWFuICU+JQogIGZpbHRlcihISC5MaWZldGltZS5HaXZpbmcgPD0gMTAwMDApICU+JQogIGZpbHRlcihISC5MaWZldGltZS5HaXZpbmcgPiAwKSAlPiUKICBtdXRhdGUoYE5vX29mX0NoaWxkcmVuYCA9IGFzLmZhY3RvcihgTm9fb2ZfQ2hpbGRyZW5gKSkgJT4lCiAgZ2dwbG90KGFlcyhISC5MaWZldGltZS5HaXZpbmcsIGZpbGwgPSBgTm9fb2ZfQ2hpbGRyZW5gKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMzApICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDEwMDAwMCwxMDAwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMDAwMDAwMDAsNTAwMCkpICsKICBnZ3RpdGxlKCJHaXZpbmcgZGlzdHJpYnV0aW9uIGFuZCBudW1iZXIgb2YgY2hpbGRyZW4iKSsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiMwMDI4NDUiLCAiIzAwY2ZjYyIsICIjZmY5OTczIikpCgoKCmBgYAoKCk1lYW4sIE1lZGlhbiwgYW5kIENvdW50IG9mIEdpdmluZyBpbiBBZ2UgUmFuZ2VzIAoKYGBge3J9CgphZ2VfcmFuZ2VfZ2l2aW5nIDwtIGRhdGFjbGVhbiAlPiUKICBncm91cF9ieShhZ2VfcmFuZ2UpICU+JQogIHN1bW1hcmlzZShhdmdfZ2l2aW5nID0gbWVhbihISC5MaWZldGltZS5HaXZpbmcsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG1lZF9naXZpbmcgPSBtZWRpYW4oSEguTGlmZXRpbWUuR2l2aW5nLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBhbW91bnRfb2ZfcGVvcGxlX2luX2FnZV9yYW5nZSA9IG4oKSkKCgpnbGltcHNlKGFnZV9yYW5nZV9naXZpbmcpCgpgYGAKCgoKCgojIyBQYXJ0IDIgLSBTdW1tYXJ5IFN0YXRpc3RpY3MKCjJhKSBQbG90dGluZyBhdmVyYWdlIGdpdmluZyBieSBhZ2UgcmFuZ2UgCgoKYGBge3J9CgphZ2VfcmFuZ2VfZ2l2aW5nIDwtCiAgYWdlX3JhbmdlX2dpdmluZyAlPiUKICBtdXRhdGUoYWdlX3JhbmdlID0gZmFjdG9yKGFnZV9yYW5nZSkpCgpnZ3Bsb3QoYWdlX3JhbmdlX2dpdmluZywgYWVzKGFnZV9yYW5nZSwgYXZnX2dpdmluZykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdD0xKSkgKyBsYWJzKHggPSAiQWdlIFJhbmdlIiwgeSA9ICJBdmVyYWdlIEdpdmluZyIpICsKICAgICAgZ2d0aXRsZSgiQXZlcmFnZSBHaXZpbmcgQ29tcGFyZWQgQWNyb3NzIEFnZSBSYW5nZXMiKQoKCmBgYAoKCjJiKSBDb3VudCBvZiBkb25vcnMgYmFzZWQgb24gYWdlIHJhbmdlIChhbm90aGVyIHdheSB0byBsb29rIGF0IGl0KQoKCmBgYHtyfQoKZ2dwbG90KGRhdGFjbGVhbiwgCiAgICAgICBhZXMoYWdlX3JhbmdlKSkgKyAKICAgICAgIGdlb21fYmFyKCkgKyAKICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3Q9MSkpICsgCiAgbGFicyh0aXRsZSA9ICJDb3VudCBvZiBBZ2UgUmFuZ2VzIiwgeCA9ICIiLCB5ID0gIiIpCiAgCgpgYGAKCjJjKSBCb3hwbG90IG9mIHRoZSBBZ2UgUmFuZ2VzIEFnYWluc3QgdGhlIExpZmV0aW1lIEdpdmluZyBBbW91bnRzIHdpdGggYSBsb2cgc2NhbGUgYXBwbGllZCAtIHRoZSByZWFzb24gd2UgYXBwbGllZCBsb2cgc2NhbGUgaXMgdG8gcmVzb2x2ZSBpc3N1ZXMgd2l0aCB2aXN1YWxpemF0aW9ucyB0aGF0IHNrZXcgdG93YXJkcyBsYXJnZSB2YWx1ZXMgaW4gb3VyIGRhdGFzZXQuIAoKCmBgYHtyfQoKZ2dwbG90KGRhdGFjbGVhbiwgYWVzKGFnZV9yYW5nZSxISC5MaWZldGltZS5HaXZpbmcsZmlsbCA9IGFnZV9yYW5nZSkpICsgCiAgZ2VvbV9ib3hwbG90KAogIG91dGxpZXIuY29sb3VyID0gInJlZCIpICsgCiAgc2NhbGVfeV9sb2cxMCgpICsKICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9NDUsaGp1c3Q9MSkpICArIGxhYnMoeCA9ICJBZ2UgUmFuZ2UiLCB5ID0gIkxpZmV0aW1lIEdpdmluZyBBbW91bnQiKSArCiAgICAgIGdndGl0bGUoIkxpZmV0aW1lIEdpdmluZyBDb21wYXJlZCBBY3Jvc3MgQWdlIFJhbmdlcyIpCiAgCgpgYGAKCjJkKSBTcGxpdHRpbmcgYnkgYWdlIGFuZCBnZW5kZXIgCgoKYGBge3J9CgoKI2NyZWF0aW5nIGJveHBsb3RzIApkYXRhY2xlYW4gJT4lIAogIGZpbHRlcihBZ2UgPCAxMDApICU+JSAjcmVtb3ZpbmcgdGhlIHdlaXJkIG91dGxpZXJzIHRoYXQgYXJlIG92ZXIgMTAwIAogIGZpbHRlcihTZXggJWluJSBjKCJNIiwgIkYiKSkgJT4lCiAgZ2dwbG90KGFlcyhTZXgsIEFnZSkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyAKICB0aGVtZV9lY29ub21pc3QoKSArIAogIGdndGl0bGUoIkFnZXMgb2YgRG9ub3JzIEJhc2VkIG9uIEdlbmRlciIpICsgCiAgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkKICAKCmBgYAoKR2l2aW5nIGJ5IGdlbmRlcgoKYGBge3J9CgojcmVtb3ZlIE5BcyBVIFgKCmRhdGFjbGVhbjIgPC0gZGF0YWNsZWFuICU+JQogIGZpbHRlcihTZXggJWluJSBjKCJNIiwgIkYiKSkgCgpxIDwtIGdncGxvdChkYXRhY2xlYW4yKSAKcSArIHN0YXRfc3VtbWFyeV9iaW4oCiAgYWVzKHkgPSBISC5MaWZldGltZS5HaXZpbmcsIHggPSBTZXgpLCAKICBmdW4ueSA9ICJtZWFuIiwgZ2VvbSA9ICJiYXIiKSAKICAKc3VtbWFyeShkYXRhY2xlYW4kc2V4X3NpbXBsZSkKCmBgYAoKTWVhbiBhZ2UgYnkgZ2VuZGVyCgpgYGB7cn0KI2JyZWFrZG93biBvZiBzZXhzIAp0YWxseShncm91cF9ieShkYXRhY2xlYW4sIFNleCkpCgpzdW1tYXJpemUoZ3JvdXBfYnkoZGF0YWNsZWFuLCBTZXgpLCAKICAgICAgICAgIGF2Z19naXZpbmcgPSBtZWFuKEhILkxpZmV0aW1lLkdpdmluZywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgIGF2Z19hZ2UgPSBtZWFuKEFnZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgIG1lZF9hZ2UgPSBtZWRpYW4oQWdlLCBuYS5ybSA9IFRSVUUpKQoKI2dyb3VwaW5nIGJ5IHNleCBhbmQgYWdlIHJhbmdlIGZvciBzbGlkZXMgCnRhbGx5KGdyb3VwX2J5KGRhdGFjbGVhbiwgU2V4LCBhZ2VfcmFuZ2UpKQoKCgpgYGAKCgoKMmUpIERpc3RyaWJ1dGlvbiBvZiBwZW9wbGUgaW4gdGhlIHN0YXRlcyB0aGF0IHRoZXkgbGl2ZS4KCmBgYHtyfQoKICBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKFN0YXRlID0gaWZlbHNlKFN0YXRlID09ICIgIiwgIk5BIiwgU3RhdGUpKSAlPiUKICBmaWx0ZXIoU3RhdGUgIT0gIk5BIikgJT4lCiAgZ3JvdXBfYnkoU3RhdGUpICU+JQogIHN1bW1hcmlzZShDb3VudCA9IGxlbmd0aChTdGF0ZSkpICU+JQogIGZpbHRlcihDb3VudCA+IDgwMCkgJT4lCiAgYXJyYW5nZSgtQ291bnQpICU+JQogIGthYmxlKGNvbC5uYW1lcyA9IGMoIkRvbm9yJ3MgU3RhdGUiLCAiQ291bnQiKSkgJT4lCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoImNvbmRlbnNlZCIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCgpgYGAKCjJmKSBMb29raW5nIGF0IGFsbCBkb25vcnMgZmlyc3QgZ2lmdCBhbW91bnQuIDc1JSBtYWRlIGEgZmlyc3QgZ2lmdCBvZiA8MTAwLiAKCmBgYHtyfQoKIG5vX25vbl9kb25vcnMgPC0gZGF0YWNsZWFuICU+JQogIGZpbHRlcihMaWZldGltZS5HaXZpbmcgIT0gMCkKICAKbmQgPC0gcXVhbnRpbGUobm9fbm9uX2Rvbm9ycyRISC5GaXJzdC5HaWZ0LkFtb3VudCwgcHJvYnMgPSBjKC4yNSwuNTAsLjc1LC45LC45OSksIG5hLnJtID0gVFJVRSkKCm5kIDwtIGFzLmRhdGEuZnJhbWUobmQpCgpuZCAlPiUKICBrYWJsZShjb2wubmFtZXMgPSAiUXVhbnRpbGUiKSAlPiUKICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpLAogICAgICAgICAgICAgICAgZnVsbF93aWR0aCA9IEYpCiAgCiAgCgoKYGBgCgoKCiMjIFBhcnQgMyAtIE1vZGVsaW5nCgpTcGxpdCBkYXRhIGFuZCBjcmVhdGUgYSBuZXcgc2V0IGZvciBlYXNpZXIgYW5hbHlzaXMKCmBgYCB7cn0KCiNjb252ZXJ0aW5nIG1hcnJpZWQgWSBhbmQgTiB0byAxIGFuZCAwIApkYXRhY2xlYW4gPC0gZGF0YWNsZWFuICU+JQogICAgICBtdXRhdGUoTWFycmllZF9zaW1wbGUgPSBpZmVsc2UoTWFycmllZCA9PSAiTiIsMCwxKSkKCgpkYXRhY2xlYW4gPC0gZGF0YWNsZWFuICU+JQogIG11dGF0ZShoaC5saWZldGltZS5naXZpbmdfZmN0ID0gYXMuZmFjdG9yKEhILkxpZmV0aW1lLkdpdmluZykpICU+JQogIG11dGF0ZShISC5MaWZldGltZS5HaXZpbmcuUGx1cyA9IGxvZyhISC5MaWZldGltZS5HaXZpbmcgKyAxKSkKCgoKI0NyZWF0aW5nIHRoZSB2ZWx2ZXQgc2V0IC0gb25seSB0aGUgYmVzdCBjYW4gZW50ZXIKCmRhdGF2ZWx2ZXQgPC0gc3Vic2V0KGRhdGFjbGVhbixzZWxlY3QgPSAtYygKICBhZ2VfcmFuZ2UsCiAgQXRobGV0aWNzLAogIEJpcnRoZGF0ZSwKICBDYXRlZ29yeS5Db2RlcywKICBDYXRlZ29yeS5EZXNjcmlwdGlvbnMsCiAgQ2hpbGQuMS5FbnJvbGwuU3RhdHVzLAogIENoaWxkLjEuSUQsCiAgQ2hpbGQuMi5JRCwKICBDaGlsZC4xLk1ham9yLAogIENoaWxkLjEuTWlub3IsCiAgQ2hpbGQuMS5TY2hvb2wsCiAgQ2hpbGQuMS5GZWVkZXIuU2Nob29sLAogIENoaWxkLjIuRW5yb2xsLlN0YXR1cywKICBDaGlsZC4yLk1ham9yLAogIENoaWxkLjIuTWlub3IsCiAgQ2hpbGQuMi5TY2hvb2wsCiAgQ2hpbGQuMi5GZWVkZXIuU2Nob29sLAogIENvbnRhY3QuUnVsZXMsCiAgRGVncmVlLlR5cGUuMSwKICBEZWdyZWUuVHlwZS4yLAogIERvbm9yLlNlZ21lbnQsCiAgQ2l0eSwKICBDb3VudHksCiAgQ29uZmVycmFsLkRhdGUuMSwKICBDb25mZXJyYWwuRGF0ZS4yLAogIENvbnRhY3QuUnVsZXMsCiAgRGVncmVlLlR5cGUuMSwKICBEZWdyZWUuVHlwZS4yLAogIEV4dHJhY3VycmljdWxhciwKICBISC5GaXJzdC5HaWZ0LkZ1bmQsCiAgSEguRmlyc3QuR2lmdC5EYXRlLAogIElELAogIEluc3RpdHV0aW9uLlR5cGUsCiAgSm9iLlRpdGxlLAogIExhc3QuQ29udGFjdC5CeS5BbnlvbmUsCiAgTGVnYWN5TGVhZGVyLi5jb21wYXNzLnNjb3JlLiwKICBNYXJyaWVkLAogIE1ham9yLjEsCiAgTWFqb3IuMiwKICBNaW5vci4xLAogIE1pbm9yLjIsCiAgcmVnaW9uLAogIHppcGNvZGVfc2xyeSwKICBTZXgsCiAgU2Nob2xhcnNoaXAsCiAgU2Nob29sLjEsCiAgU2Nob29sLjIsCiAgU3BvdXNlLkJpcnRoZGF0ZSwKICBTcG91c2UuTWFpbC5SdWxlcywKICBTdGF0ZSwKICBaaXAsCiAgemlwc2xyeV9yYW5nZQopKQoKI2RhdGF2ZWx2ZXQgPC0KI2RhdGF2ZWx2ZXRbc2FwcGx5KGRhdGF2ZWx2ZXQsIGlzLmNoYXJhY3RlcildIDwtICNsYXBwbHkoZGF0YXZlbHZldFtzYXBwbHkoZGF0YXZlbHZldCwgaXMuY2hhcmFjdGVyKV0sIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5mYWN0b3IpCgpsaWJyYXJ5KCJyc2FtcGxlIikKCmRhdGFfc3BsaXQgPC0gaW5pdGlhbF9zcGxpdChkYXRhdmVsdmV0LCBwcm9wID0gMC43NSkKCmRhdGFfdHJhaW4gPC0gdHJhaW5pbmcoZGF0YV9zcGxpdCkKZGF0YV90ZXN0IDwtIHRlc3RpbmcoZGF0YV9zcGxpdCkKCmBgYAoKCgpgYGB7cn0KcCA8LSBkYXRhdmVsdmV0ICU+JQogIGdncGxvdChhZXMoQWdlKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zPTMwLCBmaWxsID0gImJsdWUiKSArIHRoZW1lX2Vjb25vbWlzdF93aGl0ZSgpICsKICBnZ3RpdGxlKCJPdmVyYWxsIERvbm9yIEFnZSBEaXN0cmlidXRpb24iKSArIAogIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSg1LDEwMCxieSA9IDIwKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMjAsMTAwLGJ5ID0gMjApKSArIHhsaW0oYygyMCwxMDApKQoKZ2dwbG90bHkocCkKICAKcAoKZ2dwbG90KGRhdGEgPSBkYXRhdmVsdmV0LCBhZXMoeCA9IEFnZSkpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ImJsdWUiKSsgeGxpbShjKDIwLDEwMCkpCgogIAoKCmBgYAoKQW5vdGhlciBIaXN0b2dyYW0KCgpgYGB7cn0KCmRhdGF2ZWx2ZXQgJT4lCiAgZmlsdGVyKEFnZSA+PSAxMCkgJT4lCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lCiAgZ2dwbG90KGFlcyhBZ2UpKSArIGdlb21faGlzdG9ncmFtKGZpbGwgPSAiIzAwMjg0NSIsIGJpbnMgPSAyMCkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgZ2d0aXRsZSgiT3ZlcmFsbCBEb25vciBBZ2UgRGlzdHJpYnV0aW9uIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsMTAwMDAwMDAsMjAwMCkpIAoKYGBgCkFnZSBkaXN0cmlidXRpb24gYnkgZ2VuZGVyIAoKYGBge3J9CiNBZ2UgR2VuZGVyIGZpbHRlcmVkIG91dCBiZWxvdyAxNSBhbmQgYWJvdmUgOTAgLSBhbHNvIHJlbW92ZWQgVSBYIHRoZSB3ZWlyZCB2YWx1ZXMgCmRhdGF2ZWx2ZXQgJT4lCiAgZmlsdGVyKEFnZSA+PSAxNSkgJT4lCiAgZmlsdGVyKEFnZSA8PSA5MCkgJT4lCiAgbXV0YXRlKFNleCA9IGFzLmZhY3RvcihTZXgpKSAlPiUKICBmaWx0ZXIoU2V4ICE9ICJVIikgJT4lCiAgZmlsdGVyKFNleCAhPSAiWCIpICU+JQogIGdncGxvdChhZXMoQWdlLCBmaWxsID0gU2V4KSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjUpICsgdGhlbWVfZWNvbm9taXN0X3doaXRlKCkgKwogIGdndGl0bGUoIkFnZSBEaXN0cmlidXRpb24gYnkgR2VuZGVyIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsMTApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLDUwMDAwLDIwMDApKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCIjZmY5OTczIiwgIiMwMGNmY2MiKSkKYGBgCgpEb25vciBhZ2UgZGlzdHJpYnV0aW9uIGJ5IG1hcml0YWwgc3RhdHVzIAoKYGBge3J9CiNBZ2UgTWFyaXRhbCBTdGF0dXMKZGF0YXZlbHZldCAlPiUKICBmaWx0ZXIoQWdlID49IDIwKSAlPiUKICBmaWx0ZXIoQWdlIDw9IDg1KSAlPiUKICBnZ3Bsb3QoYWVzKEFnZSwgZmlsbCA9IE1hcnJpZWQpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyNSkgKyB0aGVtZV9lY29ub21pc3Rfd2hpdGUoKSArCiAgZ2d0aXRsZSgiT3ZlcmFsbCBEb25vciBBZ2UgRGlzdHJpYnV0aW9uIGJ5IE1hcml0YWwgU3RhdHVzIikgKyAKICB4bGFiKE5VTEwpICsgeWxhYihOVUxMKSArIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxMjAsNSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsNTAwMDAsMjAwMCkpICsgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoIiNmZjk5NzMiLCAiIzAwY2ZjYyIpKQpgYGAKCkxpbmVhciBNb2RlbCAKCmBgYHtyfQojVGhlc2Ugd2lsbCBmb2N1cyBvbiBwcmVkaWN0aW5nIHdoZXRoZXIgYSBjb25zdGl0dWVudCBpcyBhIGRvbm9yIG9yIG5vbi1kb25vci4gCgoKbW9kMWxtIDwtIGxtKCBMaWZldGltZS5HaXZpbmcgfiBNYXJyaWVkX3NpbXBsZSwKICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbikKCm1vZDJsbSA8LSBsbSggVG90YWwuR2l2aW5nLlllYXJzIH4gTGlmZXRpbWUuR2l2aW5nLAogICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKbW9kM2xtIDwtIGxtKCBMaWZldGltZS5HaXZpbmcgfiByZWdpb24sCiAgICAgICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pCgpzdW1tYXJ5KG1vZDFsbSkKc3VtbWFyeShtb2QybG0pCnN1bW1hcnkobW9kM2xtKQojaW5jcmVhc2luZyB0aGUgZ2l2aW5nIHllYXIgb25lIHllYXIgaW5jcmVhc2UgdG90YWwgZ2l2aW5nIGJ5IDAuMDAzNQoKCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coSEguTGlmZXRpbWUuR2l2aW5nKSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+cmVnaW9uKSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIlJlZ2lvbiIpCgoKZ2dwbG90KGRhdGEgPSBkYXRhX3RyYWluLCBhZXMoeCA9IEFnZSwgeSA9IGxvZyhISC5MaWZldGltZS5HaXZpbmcpKSkgKyBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkgKyBnZW9tX3Ntb290aChtZXRob2QgPSBsbSkgKyBmYWNldF93cmFwKH5ubWJfZGVncmVlKSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIk51bWJlciBvZiBEZWdyZWVzIikKCgpnZ3Bsb3QoZGF0YSA9IGRhdGFfdHJhaW4sIGFlcyh4ID0gQWdlLCB5ID0gbG9nKEhILkZpcnN0LkdpZnQuQW1vdW50KSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+ZG9ub3JzZWdfZmN0KSArIHRoZW1lX2NsZWFuKGJhc2Vfc2l6ZSA9IDgpICsgbGFicyh4ID0gIlgiLCB5ID0gIlkiKSArCiAgICAgIGdndGl0bGUoIkRvbm9yIFNlZ21lbnQiKQoKI1RoaXMgcGxvdCBhY3R1YWxseSBoYXMgc29tZSBpbnRlcmVzdGluZyByZXN1bHRzCmdncGxvdChkYXRhID0gZGF0YV90cmFpbiwgYWVzKHggPSBBZ2UsIHkgPSBsb2coTGlmZXRpbWUuR2l2aW5nKSkpICsgZ2VvbV9wb2ludChhbHBoYSA9IDEvMTApICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gbG0pICsgZmFjZXRfd3JhcCh+Tm9fb2ZfQ2hpbGRyZW4pICsgdGhlbWVfY2xlYW4oYmFzZV9zaXplID0gOCkgKyBsYWJzKHggPSAiWCIsIHkgPSAiWSIpICsKICAgICAgZ2d0aXRsZSgiIyBDaGlsZHJlbiIpCgoKZGF0YV90cmFpbiAlPiUgCiAgc2VsZWN0X2lmKGlzLmZhY3RvcikgJT4lIAogIGdsaW1wc2UoKQoKCmBgYAoKCgpNT1JFIE1PREVMUwoKQmlnIGxvZ2lzdGljIG1vZGVsCgpgYGB7cn0KCiMgU2V0IGZhbWlseSB0byBiaW5vbWlhbCB0byBzZXQgbG9naXN0aWMgZnVuY3Rpb24KIyBSdW4gdGhlIG1vZGVsIG9uIHRoZSB0cmFpbmluZyBzZXQKCmRvbm9yX2xvZ2l0MSA8LQogIGdsbShoaC5saWZldGltZS5naXZpbmdfZmN0IH4gTWFycmllZF9zaW1wbGUsCiAgICAgIGZhbWlseSA9ICJiaW5vbWlhbCIsCiAgICAgIGRhdGEgPSBkYXRhX3RyYWluKQoKc3VtbWFyeShkb25vcl9sb2dpdDEpCgoKZG9ub3JfbG9naXQyIDwtCiAgZ2xtKGhoLmxpZmV0aW1lLmdpdmluZ19mY3QgfiBOb19vZl9DaGlsZHJlbiwKICAgICAgZmFtaWx5ID0gImJpbm9taWFsIiwKICAgICAgZGF0YSA9IGRhdGFfdHJhaW4pCgpzdW1tYXJ5KGRvbm9yX2xvZ2l0MikKCgpgYGAKCgoKYGBge3IgbG9nfQoKCiNzdW1tYXJ5KGRhdGFfdHJhaW4kbWFqb3JfZ2lmdGVyKQojQXNzaWdubWVudF9mbGFnIHRha2VuIG91dCAtIG1heSBhZGQgYmFjawoKZG9ub3JfbG9naXQzIDwtCiAgZ2xtKG1ham9yX2dpZnRlciB+IE1hcnJpZWQgKyBOb19vZl9DaGlsZHJlbiArIGRvbm9yc2VnX3NpbXBsZSArICBUb3RhbC5HaXZpbmcuWWVhcnMgKyBubWJfZGVncmVlLAogICAgICBmYW1pbHkgPSAiYmlub21pYWwiLAogICAgICBkYXRhID0gZGF0YV90cmFpbikKCnN1bW1hcnkoZG9ub3JfbG9naXQzKQpleHAoZG9ub3JfbG9naXQzJGNvZWZmaWNpZW50cykKCiN0cmFpbmluZyBwcmVkaWN0aW9ucyBmb3IgaW4gc2FtcGxlIHByZWRzIApwcmVkc190cmFpbiA8LSBwcmVkaWN0KGRvbm9yX2xvZ2l0MywgbmV3ZGF0YSA9IGRhdGFfdHJhaW4sIHR5cGUgPSAicmVzcG9uc2UiKSAKCiN0ZXN0IHByZWRpY3RzIGZvciBPT1MgKG91dCBvZiBzYW1wbGUpCnByZWRzX3Rlc3QgPC0gcHJlZGljdChkb25vcl9sb2dpdDMsIG5ld2RhdGEgPSBkYXRhX3Rlc3QsIHR5cGUgPSAicmVzcG9uc2UiKQoKaGVhZChwcmVkc190cmFpbikKaGVhZChwcmVkc190ZXN0KQoKCgpyZXN1bHRzX3RyYWluIDwtIGRhdGEuZnJhbWUoCiAgYHRydXRoYCA9IGRhdGFfdHJhaW4gICAlPiUgc2VsZWN0KG1ham9yX2dpZnRlcikgJT4lIAogICAgbXV0YXRlKG1ham9yX2dpZnRlciA9IGFzLm51bWVyaWMobWFqb3JfZ2lmdGVyKSksCiAgYENsYXNzMWAgPSAgcHJlZHNfdHJhaW4sCiAgYHR5cGVgID0gcmVwKCJ0cmFpbiIsbGVuZ3RoKHByZWRzX3RyYWluKSkKKQoKcmVzdWx0c190ZXN0IDwtIGRhdGEuZnJhbWUoCiAgYHRydXRoYCA9IGRhdGFfdGVzdCAgICU+JSBzZWxlY3QobWFqb3JfZ2lmdGVyKSAlPiUgCiAgICBtdXRhdGUobWFqb3JfZ2lmdGVyID0gYXMubnVtZXJpYyhtYWpvcl9naWZ0ZXIpKSwKICBgQ2xhc3MxYCA9ICBwcmVkc190ZXN0LAogIGB0eXBlYCA9IHJlcCgidGVzdCIsbGVuZ3RoKHByZWRzX3Rlc3QpKQopCgpyZXN1bHRzIDwtIGJpbmRfcm93cyhyZXN1bHRzX3RyYWluLHJlc3VsdHNfdGVzdCkKCmRpbShyZXN1bHRzX3RyYWluKQpkaW0ocmVzdWx0c190ZXN0KQpkaW0ocmVzdWx0cykKCmxpYnJhcnkoJ3Bsb3RST0MnKQoKcF9wbG90IDwtCiAgZ2dwbG90KHJlc3VsdHMsCiAgICAgICAgIGFlcyhtID0gQ2xhc3MxLCBkID0gbWFqb3JfZ2lmdGVyLCBjb2xvciA9IHR5cGUpKSArCiAgZ2VvbV9yb2MobGFiZWxzaXplID0gMi41LAogICAgICAgICAgICNUb29rIHRoZSBsYWJlbHNpemUgZG93biB0byBhdm9pZCBjdXRvZmYKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKICNXZSByZW1vdmVkIHNvbWUgb2YgdGhlIGN1dG9mZnMgdG8gYXZvaWQgdGhlIG1hc2h1cCBuZWFyIHRoZSBvcmlnaW4uCgogICNDaGFuZ2VkIHRoZSB0aGVtZSB0byBhdm9pZCBjdXRvZmYgcGxvdCB2YWx1ZXMuCiAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNCkgKyAKICBsYWJzKHggPSAiRmFsc2UgUG9zaXRpdmUgUmF0ZSIsIAogICAgICAgeSA9ICJUcnVlIFBvc2l0aXZlIFJhdGUiKSArCiAgICAgIGdndGl0bGUoIlJPQyBQbG90OiBUcmFpbmluZyBhbmQgVGVzdCIpCnByaW50KHBfcGxvdCkgCgoKcF90cmFpbiA8LQogIGdncGxvdChyZXN1bHRzX3RyYWluLAogICAgICAgICBhZXMobSA9IENsYXNzMSwgZCA9IG1ham9yX2dpZnRlciwgY29sb3IgPSB0eXBlKSkgKwogIGdlb21fcm9jKGxhYmVsc2l6ZSA9IDMuNSwKICAgICAgICAgICBjdXRvZmZzLmF0ID0gYygwLjcsMC41LDAuMywwLjEsMCkpICsKIAogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gMTYpICsgCiAgbGFicyh4ID0gIkZhbHNlIFBvc2l0aXZlIFJhdGUiLCAKICAgICAgIHkgPSAiVHJ1ZSBQb3NpdGl2ZSBSYXRlIikgKwogICAgICBnZ3RpdGxlKCJST0MgUGxvdDogVHJhaW5pbmcgYW5kIFRlc3QiKQoKcF90ZXN0IDwtCiAgZ2dwbG90KHJlc3VsdHNfdGVzdCwKICAgICAgICAgYWVzKG0gPSBDbGFzczEsIGQgPSBtYWpvcl9naWZ0ZXIsIGNvbG9yID0gdHlwZSkpICsKICBnZW9tX3JvYyhsYWJlbHNpemUgPSAzLjUsCiAgICAgICAgICAgY3V0b2Zmcy5hdCA9IGMoMC43LDAuNSwwLjMsMC4xLDApKSArCiAKICB0aGVtZV9taW5pbWFsKGJhc2Vfc2l6ZSA9IDE2KSArIAogIGxhYnMoeCA9ICJGYWxzZSBQb3NpdGl2ZSBSYXRlIiwgCiAgICAgICB5ID0gIlRydWUgUG9zaXRpdmUgUmF0ZSIpICsKICAgICAgZ2d0aXRsZSgiUk9DIFBsb3Q6IFRyYWluaW5nIGFuZCBUZXN0IikKCmBgYAoKYGBge3J9Cgojc3VtbWFyeShkb25vcl9sb2dpdDMpCiNjb2VmKGRvbm9yX2xvZ2l0MykKCgojQ2FsY3VsYXRpbmcgQVVDIG9mIGJvdGgKcHJpbnQoY2FsY19hdWMocF90cmFpbikkQVVDKQpwcmludChjYWxjX2F1YyhwX3Rlc3QpJEFVQykKYGBgCgpSSURHRQoKYGBge3J9CgpsaWJyYXJ5KCdnbG1uZXQnKQpsaWJyYXJ5KCdnbG1uZXRVdGlscycpCgpyaWRnZV9maXQxIDwtIGN2LmdsbW5ldChISC5MaWZldGltZS5HaXZpbmcuUGx1cyB+IHNleF9mY3QgKyBkb25vcnNlZ19mY3QgKyBOb19vZl9DaGlsZHJlbiwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDApCgojQWxwaGEgMCBzZXRzIHRoZSBSaWRnZQpwcmludChyaWRnZV9maXQxKQpwcmludChyaWRnZV9maXQxJGxhbWJkYS5taW4pCgpwcmludChyaWRnZV9maXQxJGxhbWJkYS4xc2UpCnBsb3QocmlkZ2VfZml0MSkKCmBgYAoKQklHIFJJREdFCgpgYGB7cn0KCmxpYnJhcnkoJ2dsbW5ldCcpCmxpYnJhcnkoJ2dsbW5ldFV0aWxzJykKCmRhdGFfdHJhaW4gJT4lIG1hcChsZXZlbHMpICU+JSBtYXAobGVuZ3RoKQoKcmlkZ2VfZml0MiA8LSBjdi5nbG1uZXQoSEguTGlmZXRpbWUuR2l2aW5nLlBsdXMgfiAuLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMCkKCiNBbHBoYSAwIHNldHMgdGhlIFJpZGdlCnByaW50KHJpZGdlX2ZpdDIpCnByaW50KHJpZGdlX2ZpdDIkbGFtYmRhLm1pbikKCnByaW50KHJpZGdlX2ZpdDIkbGFtYmRhLjFzZSkKcGxvdChyaWRnZV9maXQyKQoKYGBgCgoKTEFTU08KCmBgYHtyfQoKI1VzaW5nIGN2LmdsbW5ldCBmcm9tIGNsYXNzCiNscyhkYXRhX3RyYWluKSAKI2lzLmZhY3RvcihkYXRhX3RyYWluJG1ham9yX2dpZnRlcikKI2dsaW1wc2UoZGF0YV90cmFpbiRMaWZldGltZS5HaXZpbmcpCgojZGF0YV90cmFpbiAlPiUgCiMgIHNlbGVjdF9pZihpcy5mYWN0b3IpICU+JSAKIyAgZ2xpbXBzZSgpCgoKCmxpYnJhcnkoZ2xtbmV0KQpsaWJyYXJ5KGdsbW5ldFV0aWxzKQpsYXNzb19maXQgPC0gY3YuZ2xtbmV0KEhILkxpZmV0aW1lLkdpdmluZy5QbHVzIH4gc2V4X2ZjdCArIGpvYnRpdGxlX3NpbXBsZSArIG5tYl9kZWdyZWUgKyBzY2hvb2wxX3NpbXBsZSArIGhoZmlyc3RnaWZ0X3NpbXBsZSArIG1hajFfc2ltcGxlICsgZG9ub3JzZWdfc2ltcGxlICsgTm9fb2ZfQ2hpbGRyZW4gKyBNYXJyaWVkLAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgICNBbHBoYSAxIGZvciBsYXNzbwogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMSkKCgpwcmludChsYXNzb19maXQkbGFtYmRhLm1pbikKIwpwcmludChsYXNzb19maXQkbGFtYmRhLjFzZSkKCnBsb3QobGFzc29fZml0KQoKYGBgCgoKCgpgYGB7cn0KCmNvZWYobGFzc29fZml0KQojRGVmYXVsdCBzZXR0aW5nIGlzIGxhbWJkYS4xc2UKCiNGcm9tIHRoZSBib29rIC0gc2hvd2luZyBjb252ZXJnZW5jZSB3aXRoIGxhbWJkYSB2YWx1ZXMKcGxvdChsYXNzb19maXQkZ2xtbmV0LmZpdCwgeHZhcj0ibGFtYmRhIikKI2FibGluZSh2PWxvZyhjKGxhc3NvX2ZpdCRsYW1iZGEubWluLCBsYXNzb19maXQkbGFtYmRhLjFzZSkpLCBsdHk9MikKCmBgYAoKCgoKCmBgYHtyfQoKZW5ldF9tb2QgPC0gY3ZhLmdsbW5ldChISC5MaWZldGltZS5HaXZpbmcuUGx1cyB+IHNleF9mY3QgKyBqb2J0aXRsZV9zaW1wbGUgKyBubWJfZGVncmVlICsgc2Nob29sMV9zaW1wbGUgKyBoaGZpcnN0Z2lmdF9zaW1wbGUgKyBtYWoxX3NpbXBsZSArIGRvbm9yc2VnX3NpbXBsZSArIE5vX29mX0NoaWxkcmVuICsgTWFycmllZCwKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0YV90cmFpbiwKICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IHNlcSgwLDEsIGJ5ID0gMC4xKSkKCnByaW50KGVuZXRfbW9kKQpwbG90KGVuZXRfbW9kKQoKCmBgYAoKRUxBU1RJQ05FVAoKYGBge3IgZWxhc3RpY25ldH0KCm1pbmxvc3NwbG90KGVuZXRfbW9kLCAKICAgICAgICAgICAgY3YudHlwZSA9ICJtaW4iKQoKZ2V0X2FscGhhIDwtIGZ1bmN0aW9uKGZpdCkgewogIGFscGhhIDwtIGZpdCRhbHBoYQogIGVycm9yIDwtIHNhcHBseShmaXQkbW9kbGlzdCwgCiAgICAgICAgICAgICAgICAgIGZ1bmN0aW9uKG1vZCkge21pbihtb2QkY3ZtKX0pCiAgYWxwaGFbd2hpY2gubWluKGVycm9yKV0KfQoKZ2V0X21vZGVsX3BhcmFtcyA8LSBmdW5jdGlvbihmaXQpIHsKICBhbHBoYSA8LSBmaXQkYWxwaGEKICBsYW1iZGFNaW4gPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBgW1tgLCAibGFtYmRhLm1pbiIpCiAgbGFtYmRhU0UgPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBgW1tgLCAibGFtYmRhLjFzZSIpCiAgZXJyb3IgPC0gc2FwcGx5KGZpdCRtb2RsaXN0LCBmdW5jdGlvbihtb2QpIHttaW4obW9kJGN2bSl9KQogIGJlc3QgPC0gd2hpY2gubWluKGVycm9yKQogIGRhdGEuZnJhbWUoYWxwaGEgPSBhbHBoYVtiZXN0XSwgbGFtYmRhTWluID0gbGFtYmRhTWluW2Jlc3RdLAogICAgICAgICAgICAgbGFtYmRhU0UgPSBsYW1iZGFTRVtiZXN0XSwgZXJvciA9IGVycm9yW2Jlc3RdKQp9CgpiZXN0X2FscGhhIDwtIGdldF9hbHBoYShlbmV0X21vZCkKcHJpbnQoYmVzdF9hbHBoYSkKZ2V0X21vZGVsX3BhcmFtcyhlbmV0X21vZCkKCmJlc3RfbW9kIDwtIGVuZXRfbW9kJG1vZGxpc3RbW3doaWNoKGVuZXRfbW9kJGFscGhhID09IGJlc3RfYWxwaGEpXV0KCnByaW50KGJlc3RfbW9kKQoKbWlubG9zc3Bsb3QoZW5ldF9tb2QsIGN2LnR5cGUgPSAibWluIikKCmBgYAoKUmlkZ2VzIHBsb3QgLSBjb3VsZCBiZSB1c2VmdWwgZm9yIHBsb3R0aW5nIGRvbmF0aW9ucyB2cyBkb25vciBzZWdtZW50CgpgYGB7cn0KCmxpYnJhcnkoJ2dncmlkZ2VzJykKCnN1bW1hcnkoZGF0YV90cmFpbiR2YXJpYWJsZSkKCmdncGxvdChkYXRhX3RyYWluLCBhZXMoeCA9IEhILkxpZmV0aW1lLkdpdmluZywgeSA9IHJlZ2lvbikpICsgZ2VvbV9kZW5zaXR5X3JpZGdlcyhyZWxfbWluX2hlaWdodCA9IDAuMDA1KSArIHhsaW0oYygyNTAwMCwgMTAwMDAwKSkgKyAKICAgICAgZ2d0aXRsZSgiSEggTGlmZXRpbWUgR2l2aW5nIGJ5IFJlZ2lvbiIpCgpgYGAKCmBgYHtyfQoKbGlicmFyeSgnY29ycnBsb3QnKQoKI3JlbW92aW5nIElEIHppcCBhbmQgbm9ubnVtZXJpYyAKY29ycnBsb3RfZGF0YSA8LSBkYXRhY2xlYW5bLWMoMTo0OCw1Mjo1Niw1ODo2MCw2Myw2Njo2Nyw3MDo3Miw3NDo4MSw4MzoxMzIpXQoKI0NvbnZlcnQgZnJvbSBjaGFyYWN0ZXIgdG8gbnVtZXJpYyBkYXRhIHR5cGUKY29udmVydF9mYWMybnVtIDwtIGZ1bmN0aW9uKHgpewogIGFzLm51bWVyaWMoYXMuZmFjdG9yKHgpKQp9Cgpjb3JycGxvdF9kYXRhIDwtIG11dGF0ZV9hdChjb3JycGxvdF9kYXRhLAogICAgICAgICAgICAgICAgICAgICAudmFycyA9IGMoMToxMiksCiAgICAgICAgICAgICAgICAgICAgIC5mdW5zID0gY29udmVydF9mYWMybnVtKQojbWFraW5nIGEgbWF0cml4CmNkX2NvciA8LSBjb3IoY29ycnBsb3RfZGF0YSkKCiNjcmVhdGluZyBjb3JyZWxhdGlvbgpjb2wgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCIjQkI0NDAwIiwgIiNFRTk5OTAiLCAKICAiI0ZGRkZGRiIsICIjNzdBQUVFIiwgIiM0NDc3QkIiKSkKY29ycnBsb3QoY2RfY29yLCBtZXRob2Q9ImNvbG9yIiwgY29sPWNvbCgxMDApLAogIHR5cGU9Imxvd2VyIiwgYWRkQ29lZi5jb2wgPSAiYmxhY2siLAogIHRsLnBvcz0ibHQiLCB0bC5jb2w9ImJsYWNrIiwgCiAgdGwuY2V4PTAuNywgdGwuc3J0PTQ1LCAKICBudW1iZXIuY2V4PTAuNywKICBkaWFnPUZBTFNFKQoKI2NvcnJlbGF0aW9uIG1hdHJpeAojIHBhaXJzKH5BZ2UgKyBNb250aHMuU2luY2UuTGFzdC5HaWZ0ICsgZG9ub3JzZWdfZmN0ICsgCiMgICAgIG5tYl9kZWdyZWUgKyBOb19vZl9DaGlsZHJlbiArIEhILkZpcnN0LkdpZnQuQWdlICsgSEguRmlyc3QuR2lmdC5BbW91bnQgKyBUb3RhbC5HaXZpbmcuWWVhcnMsCiMgICAgIGNvbCA9IGNvcnJwbG90X2RhdGEkSEguTGlmZXRpbWUuR2l2aW5nLAojICAgICBkYXRhID0gY29ycnBsb3RfZGF0YSwgCiMgICAgIG1haW4gPSAiRG9ub3IgU2NhdHRlciBQbG90IE1hdHJpeCIpCgojd29ydGhsZXNzLi4gCgpnZ3Bsb3QoZGF0YSA9IGNvcnJwbG90X2RhdGEsIGFlcyh4ID0gbm1iX2RlZ3JlZSwgeSA9IEhILkxpZmV0aW1lLkdpdmluZykpICsgCiAgZ2VvbV9wb2ludChhcGxoYSA9IDEvMTApKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbG9yID0icmVkIikgCgpgYGAKCgpSYW5kb20gRm9yZXN0CgpgYGB7cn0KCmxpYnJhcnkoJ3JhbmRvbUZvcmVzdCcpCgpyZl9maXRfZG9ub3IgPC0gcmFuZG9tRm9yZXN0KExpZmV0aW1lLkdpdmluZyB+IC4sIAogICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhX3RyYWluLAogICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSBjbGFzc2lmaWNhdGlvbiwKICAgICAgICAgICAgICAgICAgICAgICBtdHJ5ID0gNywKICAgICAgICAgICAgICAgICAgICAgICBuYS5hY3Rpb24gPSBuYS5yb3VnaGZpeCwKICAgICAgICAgICAgICAgICAgICAgICBudHJlZSA9IDIwMCwKICAgICAgICAgICAgICAgICAgICAgICBpbXBvcnRhbmNlPVRSVUUKICAgICAgICAgICAgICAgICAgICAgICApCgpwcmludChyZl9maXRfZG9ub3IpCgoKYGBgCgpgYGB7ciwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDZ9Cgp2YXJJbXBQbG90KHJmX2ZpdF9kb25vciwgc29ydCA9IFRSVUUsIAogICAgICAgICAgIG4udmFyID0gNSwKICAgICAgICAgICB0eXBlID0gMiwgY2xhc3MgPSBOVUxMLCBzY2FsZSA9IFRSVUUsIAogICAgICAgICAgIG1haW4gPSBkZXBhcnNlKHN1YnN0aXR1dGUocmZfZml0X2Rvbm9yKSkpCgoKYGBgCgpgYGB7cn0KCmxpYnJhcnkoJ3JhbmRvbUZvcmVzdEV4cGxhaW5lcicpCgpwbG90X21pbl9kZXB0aF9kaXN0cmlidXRpb24oCiAgcmZfZml0X2Rvbm9yLAogIGsgPSAxMCwKICBtaW5fbm9fb2ZfdHJlZXMgPSAwLAogIG1lYW5fc2FtcGxlID0gInRvcF90cmVlcyIsCiAgbWVhbl9zY2FsZSA9IEZBTFNFLAogIG1lYW5fcm91bmQgPSAyLAogIG1haW4gPSAiRGlzdHJpYnV0aW9uIG9mIG1pbmltYWwgZGVwdGggYW5kIGl0cyBtZWFuIgopCgpgYGAKCgoKCmBgYHtyfQojU3BsaXR0aW5nIENhdGVnb3J5IG91dCB0byBjaGVjayBpZiB0aGUgY2F0ZWdvcnkgaXMgdXNlZnVsIGZvciBhbmFseXNpcwpkYXRhX2NhdGVnb3J5X3NwbGl0X291dCA8LSBkYXRhY2xlYW4gJT4lCiAgbXV0YXRlKENhdGVnb3J5LkNvZGVzID0gdHJpbShzdHJzcGxpdChhcy5jaGFyYWN0ZXIoQ2F0ZWdvcnkuQ29kZXMpLCAifCIsIGZpeGVkID0gVFJVRSkpKSAlPiUKICB1bm5lc3QoQ2F0ZWdvcnkuQ29kZXMpICU+JSBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gQ2F0ZWdvcnkuQ29kZXMsdmFsdWVzX2Zyb20gPUNhdGVnb3J5LkNvZGVzLCB2YWx1ZXNfZm4gPSBsZW5ndGgpCgoKYGBgCg==